I spent most of my career in bash. Then zsh had its moment, and I jumped ship like everyone else. Then I found fish, and I stopped looking.
fish—the Friendly Interactive Shell—is one of those tools where you try it for a day and wonder why you put up with anything else. It’s not trying to be POSIX-compatible. It’s not trying to be backwards-compatible with decades of shell scripting conventions. It’s just trying to be a good interactive shell. And it is.
What Makes fish Different
The first time you open fish and start typing a command, something surprising happens: it works. No configuration needed. No plugins to install. No .zshrc copied from some random GitHub repo.
Syntax highlighting in real time. As you type, valid commands are green, invalid ones are red. Paths that exist look different from paths that don’t. You see errors before you hit Enter. This alone saved me more time than I’d like to admit.
Autosuggestions from your history. Start typing a command you’ve run before, and fish suggests the rest in faded text. Press the right arrow key to accept. It’s like your shell remembers what you were trying to do—because it does.
Tab completion that actually helps. fish parses man pages to generate completions. Out of the box. No custom completion scripts needed for most tools. Type git checkout and press Tab, and you’ll see your branches listed. Type ssh and Tab, and you’ll see your hosts from ~/.ssh/config.
Sane scripting syntax. No more if [ -f "$file" ]; then ... fi. In fish, it’s if test -f $file ... end. No double brackets. No forgotten semicolons. The syntax reads like English instead of encrypted Perl.
Installing fish
On macOS with Homebrew:
brew install fish
That’s it. You can start using it immediately by typing fish in your current terminal.
When you’re ready to commit, make it your default shell:
# Add fish to allowed shells
echo $(which fish) | sudo tee -a /etc/shells
# Set as default
chsh -s $(which fish)
Open a new terminal window, and you’re in fish.
My Setup
I’ve been running fish for a while now, and my config is minimal. That’s the point—fish gives you so much out of the box that you don’t need to spend a weekend configuring it.
The Config
My config.fish is about a dozen lines. Some PATH entries, an environment variable or two—that’s it. No aliases. No giant function library. No 200-line config file copied from someone’s dotfiles repo.
fish helps keep things minimal with a couple of nice touches. fish_add_path manages your PATH idempotently—running it twice doesn’t add duplicates. No more manually checking if a path already exists before appending it. No more . ~/.bash_profile and ending up with a PATH a mile long. And set -Ux creates universal exported variables that persist across sessions, so you don’t need to clutter your config with things that only need to be set once.
Plugin Manager: Fisher
I use Fisher for managing plugins. It’s lightweight—just a single function file—and installs plugins from GitHub repos without any ceremony:
# Install Fisher
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher
# Install a plugin
fisher install jethrokuan/z
Plugins are tracked in ~/.config/fish/fish_plugins, a plain text file. Easy to version control, easy to replicate on another machine.
Plugins
I only use two plugins beyond Fisher itself:
z — Directory jumping based on frecency (frequency + recency). Type z proj and it jumps to whichever directory matching “proj” you visit most often. I use this dozens of times a day. It’s the kind of tool that becomes so natural you forget it’s a plugin.
puffer-fish — Adds a few shell expansion shortcuts. Type .. and it expands to ../. Keep typing dots and it keeps going up. Press ! to recall the last command. Small things, but they add up.
That’s the full plugin list. Two plugins. I’ve tried others and removed them—most of what fish provides out of the box makes extra plugins unnecessary.
Prompt: Starship
For the prompt, I use Starship—a fast, cross-shell prompt written in Rust. It shows the current directory, git branch and status, and uses a Dracula color scheme that matches my terminal theme.
Install it with Homebrew:
brew install starship
Add one line to your config.fish:
starship init fish | source
Starship’s configuration lives in ~/.config/starship.toml. I keep it simple—directory path, git info, a minimal prompt symbol. Nothing fancy. A shell prompt should stay out of the way and give you the info you need at a glance.
What About Scripts?
Here’s the one honest caveat: fish is not POSIX-compatible. Your bash scripts won’t run in fish, and fish scripts won’t run in bash.
But that doesn’t matter as much as you’d think. Shell scripts have a shebang line (#!/bin/bash or #!/bin/sh) for a reason—they specify their interpreter. Changing your interactive shell doesn’t break your existing scripts. They’ll keep running in bash just fine.
For interactive use—the stuff you type at the prompt every day—fish’s syntax is better. It’s more consistent, more readable, and harder to mess up. The tradeoff is worth it.
Getting Started
If you’re curious, just install it and try it. You don’t have to change your default shell right away. Run fish from your current terminal and poke around. The official tutorial is genuinely good—short, clear, and it covers everything you need to know in about 15 minutes.
A few things that’ll make you feel at home fast:
fish_configopens a web-based configuration UI in your browser. Yes, really. You can pick colors, prompts, and functions from a graphical interface. It’s a shell that respects your time.- Functions over aliases. In fish, you create functions instead of aliases.
function ll; ls -la $argv; endis clearer than bash’s alias syntax, and functions can do more. - Universal variables.
set -Ucreates variables that persist across all sessions and survive restarts. No more editing config files to set an environment variable.
Why I Stayed
I’ve been through enough shell hype cycles to be skeptical. Every few years, a new shell promises to change everything—xonsh, nushell, whatever the latest one is. Some of them are genuinely interesting. But fish isn’t trying to reinvent computing. It’s just a really good interactive shell that works the way you’d expect a modern tool to work.
No configuration needed to get a great experience. A small, stable plugin ecosystem. Excellent documentation. Active development without breaking changes every release.
It’s the kind of tool I install on a new machine, configure in five minutes, and then forget about—because it just works. That’s the highest compliment I can give a shell.