Goofing around with fish (the shell)
I've been recently nerd-sniped by a geek friend about fish-shell (and that really says it all about what kind of person I am).
Anyways.. I've been a zsh and oh-my-zsh user for a long time now, way before zsh was promoted as the default shell on macOS. It's been a fun ride, full of autocompletions, fancy themes and quick history browsing. I quite enjoyed it, especially compared to the "duller" bash. But since I'm always up for trying out something new and living my digital life on the edge, I decided to give it a try for a while.. especially considering that at work I'm forced to use tcsh.
This brief post is a recap of my experience after playing around with fish
for a few weeks.
Autocompletion
The main "selling point" of fish is that it's dev friendly and provides an interactive autocompletion experience. It's hard to describe exactly how it feels while typing, that's something you have to try to get familiar to.. here's a quick gif to give you a hint:
One important thing to keep in mind is that fish is not POSIX compliant. This might or might not be a big deal, but it's worth knowing that many things could just change because the writers/mantainers decided it wasn't worth following these standards and went their own way.
If you're interested, I definitely recommend visiting http://fishshell.com/docs/current/tutorial.html .
Variables
Variables are definited with the set
command.
$ set myvar 'Something'
$ echo $myvar
Something
Environment vars are set via set -x
:
$ set -x MY_ENV_VAR 'Something'
$ env | grep MY_ENV_VAR
MY_ENV_VAR=Something
Every variable in fish is just a list: it can contain 0, 1 or more elements. List indexing starts at 1.
$ set myvar 'Something'
$ echo $myvar[1]
Something
For example, to get the first or last path in your $PATH
var, you can do:
$ echo $PATH[1]
/Users/vvzen/miniconda3/bin
$ echo $PATH[-1]
/Users/vvzen/miniconda3/condabin
Since $PATH
is a string variable in bash, you would do instead:
$ echo $PATH | tr ":" "\n" | head -n 1
$ echo $PATH | tr ":" "\n" | tail -n 1
Python programmers will be immediately familiar with fish' negative indexing approach to get the last element. Accessing the first element with index 1 is a weird choice (it gave me matlab nightmares), but it is what it is.
You also have slices. This:
$ echo $PATH[1..4]
will print the first four elements in your $PATH. In bash, you could achieve something similar like this:
$ echo $PATH | tr ":" "\n" | head -n 4
But indexing in the middle
$ echo $PATH[3..4]
starts to get a bit longer:
$ echo $PATH | tr ":" "\n" | tail -n 4 | tail -n 2
even though one might argue that isn't something that you need to do very often.
Substitution
Backticks are bad, long live parenthesis (no need for the dollar sign).
$ echo I\'m (whoami) and it\'s (date +%H:%M:%S)
prints
I am vvzen and it's 14:16:52
In bash this would be:
$ echo I\'m $(whoami) and it\'s $(date +%H:%M:%S)
or
$ echo I\'m `whoami` and it\'s `date +%H:%M:%S`
Commands are not substituted within strings. You need to concatenate them:
$ echo "I am (whoami)"
prints
I am (whoami)
..but
$ echo "I am" (whoami)
prints
I am vvzen
Command substitutions can also be used to create lists, since each \n
in your string will be used to split between elements.
For example:
$ set my_list (echo -e "value1\nvalue2")
$ echo $my_list[1]
value1
$ echo $my_list[2]
value2
Writings newlines.. in one line!
This is really neat. You can basically write multiple lines in the same command, like this:
You just write whatever you need as you would normally do, no need to manually indent since fish will adjust the indentation for you as you type the right keywords:
To give a silly example, to setup the required env and launch Houdini (on a mac), you could write the following from an interactive session:
$ function launch_latest_houdini
pushd /Applications/Houdini/Current/Frameworks/Houdini.framework/Versions/Current/Resources/
set -x HFS $PWD
set -x H $HFS
set -x HB "$H/bin"
set -x HDSO "$H/../Libraries"
set -x HH "$H/houdini"
set -x HHC "HH/config"
set -x HHP "$HH/python2.7libs"
set -x HT "$H/toolkit"
set -x HSB "$HH/sbin"
set -x TEMP /tmp
set -x JAVA_HOME /Library/Java/Home
set -x PATH $HB $HSB $PATH
set -x HOUDINI_MAJOR_RELEASE 18
set -x HOUDINI_MINOR_RELEASE 5
set -x HOUDINI_BUILD_VERSON 351
set -x HOUDINI_VERSION "$HOUDINI_MAJOR_RELEASE.$HOUDINI_MINOR_RELEASE.$HOUDINI_BUILD_VERSION"
set -x HOUDINI_BUILD_KERNEL "19.3.0"
set -x HOUDINI_BUILD_PLATFORM (sw_vers -productName) (sw_vers -productVersion)
set -x HOUDINI_BUILD_COMPILER "10.0.1.10010046"
popd
echo "The Houdini $HOUDINI_VERSION environment has been initialized."
echo "Launching houdini.. 🍥"
houdini -indie -foreground
end
and then type
$ funcsave launch_latest_houdini
which saves the function in ~/.config/fish/functions/launch_latest_houdini.fish
, so that you can reuse it in the future.
The best thing is that you can literally just copy-paste the above^ snippet from this blog post it into your fish shell and just run it!
No need to open an editor, write a file, etc..
Wrapping it up
PROS
- Brings the fun back to the shell scripting!
- Slick ux that can considerably speed up your daily tasks
- Very good documentation
- Great as your own personal shell
CONS
- Adds another thing that could go wrong in the mix
- Not production proof. Not advised for running build tasks, etc..
- You need to re-learn a whole new syntax
- Smaller user base compared to bash, zsh, etc.. That means that you'll probably have to solve your issues by yourself!
Postscript (17/05/2021)
I tried running houdini from fish and I noticed a lot of crashes tied to memory errors/segmentation faults, even on very a simple scene. Probably it's unrelated, and it's just Houdini 18.5 being buggy, but as soon as I run it from the classic launch or from bash these errors seemed to decrease. So I think that the advice regarding "don't use it in production" holds true.