---
layout: post
title: 'From Bash to Fish: fishing after 15 years'
date: 2025-02-28 04:23 +0000
categories: ["bash", "fish", "shell"]
tags: ["bash", "fish", "shell"]
author: edu4rdshl
image:
  path: /bash-to-fish.png
  alt: Image of the Bash and Fish logos. Image by the author.
excerpt: Having the right shell can make a big difference in your productivity. I switched from Bash to Fish after 15 years and I'm loving it. Really loving it.
---

# Introduction

I've been using [Bash](https://www.gnu.org/software/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](https://www.zsh.org/) 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](https://fishshell.com/) 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](https://fishshell.com/), 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](https://fishshell.com/docs/current/fish_for_bash_users.html)

I migrated all of my functions and all in about 2 hours, so it isn't that hard. The official documentation for [Fish](https://fishshell.com/) can be found [here](https://fishshell.com/docs/current/index.html).

## Installing requirements

We are going to install [Fish](https://fishshell.com/), [Fisher](https://github.com/jorgebucaran/fisher), and [oh-my-posh](https://ohmyposh.dev/). On ArchLinux, you can install the packages with:

```sh
sudo pacman -S fish fisher
```

Oh-My-Posh is available [on the AUR](https://aur.archlinux.org/packages?O=0&K=oh-my-posh), you can install it with your AUR helper.

```sh
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.

**Note:** after using oh-my-posh for some time, I discovered some issues with plugins/functions that modify the prompt. I ended up replacing oh-my-posh with [Tide](https://github.com/IlanCosman/tide), which is better overall in terms of simplicity and performance.

### Main configuration file

Here's my `~/.config/fish/config.fish`:

{% raw %}
```sh
# 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
```
{% endraw %}

### 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):

{% raw %}
```sh
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
```
{% endraw %}

### Aliases

Aliases use a slightly different syntax in Fish. Here's an example of a few aliases that I use:

{% raw %}
```sh
# 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'
```
{% endraw %}

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](https://fishshell.com/docs/current/completions.html).

### Plugins

Fish has a nice plugin system, you can use [Fisher](https://github.com/jorgebucaran/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](https://github.com/jethrokuan/z): A "smart" cd command that learns your habits. TLRD: you have entered `~/Documents/Projects/Project1` several times, you can just type `z pro 1` and it will take you to `~/Documents/Projects/Project1`.
- [bass](https://github.com/edc/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:

```sh
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](https://ohmyposh.dev/) 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](https://ohmyposh.dev/docs/themes#easy-term) theme. See the [Main configuration file](#main-configuration-file) section to see how to initialize it.

### Changing the shell

To change the shell, you can use the following command:

```sh
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:

![Fish terminal](../_imgs/gnome-terminal-fish.png)

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!
