From Bash to Fish: fishing after 15 years
Introduction
I’ve been using Bash for over 15 years. It’s THE BEST shell out there. Not by technical merit, but by sheer inertia. It’s the default shell on almost every system, every script is written for bash, a lot of systems simply symlink sh
to bash
, so it’s what I’ve always used and what I’ve always recommended, it was a very good ride and can’t blame bash for absolutely anything.
But things change, more modern alternatives appear, new useful features are added, features that will probably never be added to bash because of compatibility reasons. Time ago, I decided to give Zsh a try, and honestly, it was a disaster. I’m almost sure that it was my fault, I didn’t give it enough time, etc; but imo, the Zsh defaults are a mess, not intuitive at all, and therefore not good for someone too used to bash (who is a little nervous to leave the shell he’s used for 15 years).
After some time investigating, and seeing that distros like ArchLinux uses Fish as the default shell on the live ISO, I decided to give it a try. And I’m loving it.
Why Fish?
The first thing that I loved about Fish, is how ready-to-use it’s out of the box. The default configuration is perfect:
- Auto-suggestions just works.
- Syntax highlighting is there.
- Your bash history is automatically imported.
- A nice plugin system.
- A nice configuration system respecting the XDG Base Directory Specification.
- A nice help system.
Migrating from Bash to Fish
Migrating from Bash to Fish is not hard, but it takes a bit of work if you have a bunch of bash-specific functions, aliases, etc. I had a lot of them, and I had to rewrite all of them. You can use some AI to help you with that (most IAs do a decent job for trivial tasks like it), if you don’t want to do it manually. Here’s a nice summary on the Fish documentation that will help you to understand the Fish syntax easier: Fish for bash users
I migrated all of my functions and all in about 2 hours, so it isn’t that hard. The official documentation for Fish can be found here.
Installing requirements
We are going to install Fish, Fisher, and oh-my-posh. On ArchLinux, you can install the packages with:
1
sudo pacman -S fish fisher
Oh-My-Posh is available on the AUR, you can install it with your AUR helper.
1
paru -S oh-my-posh-bin
Configuration
The Fish configuration is stored in ~/.config/fish/
. The main configuration file is ~/.config/fish/config.fish
. You can also have a ~/.config/fish/functions/
directory to store your functions, and a ~/.config/fish/completions/
directory to store your completions. There’s also a ~/.config/fish/conf.d/
directory to store your drop-in configuration files which gets sourced when Fish starts.
Main configuration file
Here’s my ~/.config/fish/config.fish
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Disable the greeting, thanks fish
set fish_greeting ""
# Environment variables (Always set)
# Use neovim as the default editor
set -x EDITOR nvim
set -x VISUAL nvim
set -x DOTNET_CLI_TELEMETRY_OPTOUT 1
# Set PATH correctly (Appending user paths after system paths)
set -x PATH $PATH \
$HOME/.local/bin
# Run only in interactive sessions
if status is-interactive
# Initialize Oh My Posh
oh-my-posh init fish --config /usr/share/oh-my-posh/themes/easy-term.omp.json | source
end
Functions
Here’s an example of a function that I use to join Podman containers (docker is also supported, in my case the docker command is an alias to podman):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function dexec
set -l container ""
set -l user "root"
set -l shell "bash"
# Parse options
argparse c= u= s= -- $argv
or return 1
if set -q _flag_c
set container $_flag_c
end
if set -q _flag_u
set user $_flag_u
end
if set -q _flag_s
set shell $_flag_s
end
# If no container was specified, list and let the user choose
if test -z "$container"
echo "No container specified. Listing running containers..."
set -l containers (docker ps --format "{{.Names}}")
if test -z "$containers"
echo "No running containers found."
return 1
end
echo "Select a container:"
for i in (seq (count $containers))
echo "$i) $containers[$i]"
end
read -P "Select a container: " container_index
if test -n "$containers[$container_index]"
echo "You selected: $containers[$container_index]"
set container $containers[$container_index]
else
echo "Invalid selection."
return 1
end
end
echo "Executing '$shell' as user '$user' in container '$container'"
docker exec -u "$user" -it "$container" "$shell"
end
Aliases
Aliases use a slightly different syntax in Fish. Here’s an example of a few aliases that I use:
1
2
3
4
5
6
7
8
9
10
# Use `eza` instead of `ls`
alias ls "eza"
# Upload Gists
alias gist "gh gist create"
# Some ls aliases
alias ll 'eza -l'
alias la 'eza -la'
alias l 'eza -hF'
So, just migrate your aliases and put the resulting file(s) in ~/.config/fish/conf.d/
.
Completions
Fish automatically searches through any directories in the list variable $fish_complete_path
, and any completions defined are automatically loaded when needed. Most software already have Fish completions, but if you need to create your own, see the official documentation.
Plugins
Fish has a nice plugin system, you can use Fisher to manage your plugins. It’s very easy to use, and you can install plugins with a single command. Here’s a list of some plugins that I use:
- z: A “smart” cd command that learns your habits. TLRD: you have entered
~/Documents/Projects/Project1
several times, you can just typez pro 1
and it will take you to~/Documents/Projects/Project1
. - bass: A utility that makes it easy to use utilities written for bash in fish shell.
To install them, you can use the following commands:
1
2
fisher install jethrokuan/z
fisher install edc/bass
Oh My Posh
As I was doing a shell migration after 15 years, why not change to a cool prompt too? I decided to give Oh My Posh a try. It’s a prompt theme engine for any shell: Bash, Zsh, Fish, and even Powershell. Well, it was designed for Powershell, initially. I’m using the easy-term theme. See the Main configuration file section to see how to initialize it.
Changing the shell
To change the shell, you can use the following command:
1
chsh -s /usr/bin/fish $USER
After that, you need to log out and log in again.
Results
This is how my terminal looks now:
I’m loving Fish, it’s a great shell, and I’m happy with my decision to switch. I’m more productive, and I’m having fun with it.
Conclusion
If you’re a bash user, I recommend you to give Fish a try. It’s a great shell, and you’ll probably love it.
Happy fishing!