fish is a shell: the program that reads the commands you type in a terminal and runs them. It helps as you type, with suggestions, colour-coded mistakes and Tab completions that describe each option, but its scripting language is not bash's.
The terminal is the window, the shell is the program inside
The two words get used as if they meant the same thing. A terminal is the app that opens a window, draws text in it and passes on your key presses: Terminal on macOS, Windows Terminal, the panel in VS Code. A shell is the program running inside that window. It prints the prompt, reads the line you type, works out which program you mean, starts it and hands over your arguments.
bash, zsh (the macOS default) and fish are all shells, and any of them can run in any terminal. You can start one inside another, which makes fish easy to try without changing a setting:
brew install fish # macOS
sudo apt install fish # Debian or Ubuntu
fish # start it
exit # back to your old shellfish greets you with 'Welcome to fish, the friendly interactive shell', the phrase its name is short for.
What fish does while you type
Most shells wait for a finished line and then report what went wrong. fish reacts to each key press. zsh can do much of this too, but only after you install and configure plugins. In fish it is built in and switched on.
Autosuggestions
As you type, fish shows the rest of a likely command in grey after the cursor. The suggestions come from your command history, from completions and from file paths that exist. Type two letters of a long command you ran last week, and fish offers the whole line back:
The right arrow (or Ctrl+F) accepts the whole suggestion, and Alt with the right arrow accepts one word of it. A suggestion never runs unless you accept it: keep typing and it changes or disappears.
Syntax highlighting
fish colours the command line as you edit it. A command name that doesn't exist is red, and it changes colour the moment the name matches a real command, so you see a typo before you press Enter. Here git is misspelt and nothing has run yet:
A file path that exists is underlined, which catches a wrong folder name the same way.
Tab completions with descriptions
Press Tab and fish lists what can come next: subcommands, options, file names, Git branches. Each entry has a short description beside it, so you can pick an option without opening the manual. After git and a space, Tab gives this:
Press Tab again to step through the list. fish ships with completions for around a thousand commands. For anything else, it reads the manual pages ('man pages') installed on your machine and builds completions from the options they describe.
Abbreviations
An abbreviation is a short word that fish replaces with a longer command as you type. Define one with abbr, then type it and press Space or Enter:
An alias works differently: the short name stays on the line and is expanded out of sight. With an abbreviation you see the expanded command before it runs, you can edit it, and your history records the full git checkout line. An abbreviation typed at the prompt lasts only until you close that shell. Save it in the config file to keep it.
Configuring fish
fish reads ~/.config/fish/config.fish each time it starts. It is fish's version of .bashrc or .zshrc, and fish reads neither of those. A small one looks like this:
# ~/.config/fish/config.fish
abbr -a gco git checkout
abbr -a gst git status
set -gx EDITOR vim
fish_add_path ~/binset -gx creates a variable that is global (g) and exported (x), meaning the programs you start can read it. fish_add_path adds a folder to your PATH, the list of places the shell looks for commands, unless it is already there.
Colours and the prompt don't need a text editor. Run fish_config and fish opens a page in your browser, served from your own machine (it needs Python installed), where you preview themes and prompts and click to apply one. fish_config theme show prints the same themes in the terminal.
fish is not bash
bash and zsh both follow the POSIX shell standard closely enough that most lines work in either. fish deliberately does not. Its language was designed to be more consistent, and the cost is that syntax you copy from a bash tutorial may not run.
Everyday commands are the same: cd, ls, git status, pipes with |, redirects with > and chaining with &&. The differences are in the scripting parts:
| Task | bash | fish |
|---|---|---|
| Set a variable | name=tuna | set name tuna |
| Export it | export name=tuna | set -gx name tuna |
| Last exit code | $? | $status |
| End a block | fi, done | end |
A loop shows the pattern. In bash:
for f in *.mov; do
echo "Converting $f"
doneIn fish there is no do, and every block closes with end:
for f in *.mov
echo "Converting $f"
endWhen a bash line fails, fish's error usually names the fix:
> TUNA=fresh
fish: Unsupported use of '='. In fish, please use
'set TUNA fresh'.Running bash code anyway
Switching to fish doesn't break the bash scripts you already have:
- A script file whose first line is
#!/bin/bashstill runs in bash, whichever shell you start it from. That first line, the 'shebang', names the program that runs the file. - For a one-line command from a tutorial, wrap it:
bash -c 'the line'. Or typebash, paste the line, andexitwhen you are done. - Setup instructions that say 'add this to your
.bashrc' need the fish version inconfig.fish. Most tools that hook into the shell document a fish line next to the bash and zsh ones, including zoxide, fzf and atuin from these seven terminal tools.
When to use it
fish works best as the shell you type into all day on your own machine. That is where suggestions and completions save time, and where nobody else depends on your syntax.
Keep writing scripts in bash or plain sh when anything else will run them: a server, a CI job, a teammate's laptop. Those machines have sh and may not have fish.
If you want the features but need bash-style syntax at the prompt, zsh with autosuggestion and syntax-highlighting plugins gets close, in exchange for the setup.
To make fish your default shell, add the path printed by which fish to /etc/shells, then run chsh -s with that path.
Common mistakes
- Expecting your old setup to carry over. fish ignores
.bashrcand.zshrc, so aliases,PATHchanges and tool setup lines must be added again inconfig.fish. - Defining an abbreviation at the prompt and finding it gone in the next window. Put the
abbrline inconfig.fish. - Pasting
name=valueor$?from a bash answer. Useset name valueand$status. - Saving a script in fish syntax for other people to run. It only works where fish is installed.
Key takeaways
- The terminal is the window. The shell is the program inside it that reads and runs your commands, and fish is one such shell.
- Autosuggestions, syntax highlighting and described Tab completions work in fish with no plugins or setup.
- An abbreviation expands on the line as you type, so you see the full command and your history records it.
- fish has its own scripting language. A bash script with a
#!/bin/bashfirst line still runs in bash. - Use fish at the prompt, and bash or
shfor scripts that other machines run.