I have been a big fan of the fish shell lately mostly because it delivers what it promises; works out of the box™️.
The obvious downside to fish is that it is non-standard POSIX sh
– meaning some (not all!) of the 1-line scripts you find on the Internet may not work.
I use to be a pretty big zsh fan but I hit enough oddities with my setup that I gave up in anger one day. 😤
In practice, it should not that bad since scripts can set a shebang like #! /usr/bin/env bash
to make sure they are portable. In reality, I’ve been bit too much by tools that run commands without explicitly setting the shell and relying on my login $SHELL
. 🙅♂️🤯
Here is a little trick I’ve used to get the best of both.
-
Set your login shell to bash, even though we intend to use fish.
users.extraUsers.fmzakari = { isNormalUser = true; shell = pkgs.bash; extraGroups = ["wheel" "networkmanager"]; description = "Farid Zakaria"; };
-
Set your
.bash_profile
or (.bashrc
conditional on interactive shell) to exec into fish.bash = { enable = true; initExtra = '' # I have had so much trouble running fish as my login shell # instead run bash as my default login shell but just exec into it. # Check if the shell is interactive. if [[ $- == *i* && -z "$NO_FISH_BASH" ]]; then exec ${pkgs.fish}/bin/fish fi ''; };
-
Create a bash function in fish that starts bash with the secret environment variable
NO_FISH_BASH
so that we don’t get into an endless loop.programs.fish = { enable = true; functions = { # to avoid going into a loop from bash -> fish -> bash # set the environment variable which stops that. bash = { body = '' NO_FISH_BASH="1" command bash $argv ''; wraps = "bash"; }; }; };
Now when you start your shell, via an interactive session, it will automatically exec into a fish shell 🎉.
AND
Your $SHELL
remains bash, which means that any non-interactive use by programs will get the common bash they unfortunately implicitly rely on.
❯ ps -p $fish_pid
PID TTY TIME CMD
71116 pts/2 00:00:00 fish
❯ echo $SHELL
/run/current-system/sw/bin/bash
❯ bash -c "echo $SHELL"
/run/current-system/sw/bin/bash