Fish is my shell of choice. I’ll never fully stop using bash because remote servers have it as default, but I use fish
on my personal machines.
I like it because it’s designed for interactive use, configurable, has good defaults, and fish are cool…
Here are some features I use and how I configure them.
Abbrs are like bash aliases but less arcane because they expand after you type them and hit space, showing what they do. Here’s an example:
abbr -a g git
Now whenever I type g
, it expands to git
. This is nice because I can see what the alias is doing before I execute the command, it makes my shell history more legible, and anybody looking on knows what exactly my short commands are doing.
fish
used to have more friendly abbr system (pre fish 3.6), in my opinion. They added a bunch of features and made it somewhat less automatic. Here are the abbr features I liked prior to fish 3.6:
Currently I don’t have this “universal” style of abbr working, but I do have them persisting to version control now.
Fish never tracked abbrs in version control; they are usually pretty simple, just a text substitution usualy. But I have so many of them, and some of them are nontrivial, so I create and save them using a function called abbr-add
.
Functions in fish use a scripting language similar to ruby:
function mkdir-cd --argument dir
mkdir -p -- $dir
and cd -- $dir
end
Here we have a few features:
--argument dir
$dir
and <command>
only runs the following command if the previous one succeedsFish supports the bash style &&
and ||
as well.
Note that functions can change the environment like the current working directory, unlike bash, for which you have to use source
; otherwise this cd
would have no effect.
Rather than naming my functions short names to make them easier to type, I give them descriptive names like mkdir-cd
(makes a directory and changes to that directory), and use abbreviations to type it quickly. For example abbr-add -a mc mkdir-cd
.
Here’s another function example:
function wip
if git diff --cached --quiet
git add .
end
git commit --no-verify -m "wip $argv"
end
This one uses $argv
to access any arguments following the command. This allows me to type wip refactoring css
without having to quote the message.
One last convention I’ll mention is how I implement default arguments. These aren’t supported natively in fish, but I start an argument with _
to indicate that this is the “raw” value that was passed on to a function, then use that value to define the version without the leading underscore. In the following example, _destination
is an optional argument variable, and its default value is set on the first line of the function (using my default
function):
function clone-cd --argument url _destination
set destination (default $_destination (repo-from-url $url))
if file-exists $destination
cd $destination && git pull
return
end
git clone --depth=1 $url && cd $destination
end
If it is invoked as clone-cd <repository> <directory>
, it will clone the repository into the specified directory. If no second argument is passed, it will use the repository url to come up with the directory name.
Note that url
does not have a leading underscore since it is a required parameter and thus has no defaulting behavior.
Fish has a robust event system. Functions that have --on-event <event>
as part of their definition will run when the event happens. The only thing I do for this for now is to source my ~/.profile
and ~/.config/fish/config.fish
after editing them. Here is the implementation, which I put in my ~/.config/fish/config.fish
.
function postexec-source-profile --on-event fish_postexec
set command_line (echo $argv | string collect)
if string match -qr "^$EDITOR " "$command_line"
set file (echo $command_line | coln 2 | string replace '~' $HOME)
set fish_config_files ~/.profile ~/.config/fish/config.fish
if contains $file $fish_config_files
echo -n "Sourcing "(echo $file | unexpand-home-tilde)"... "
source $file
echo done.
end
end
end
Like other shells, fish has programmable tab completion. Here’s a completion I use for my function echo-variable
, which I put in ~/.config/fish/completions/echo-variable.fish
:
complete -x -c echo-variable -a '(set | coln 1)'