The friendly interactive shell dates from 2005. It deliberately drops POSIX compatibility in favour of a cleaner syntax and good defaults: autosuggestions from history, syntax highlighting, and completions generated from man pages all work out of the box, with no plugins. fish 4.0 (early 2025) was a full rewrite from C++ to Rust, and the 4.x series continues from there.
Syntax compared with bash¶
| bash | fish |
|---|---|
VAR=value |
set VAR value |
export VAR=value |
set -gx VAR value (export VAR=value also works) |
$? |
$status |
$(cmd) |
(cmd) or $(cmd) |
if [ -f x ]; then ...; fi |
if test -f x; ...; end |
for f in *; do ...; done |
for f in *; ...; end |
func() { ...; } |
function func; ...; end |
$1, $@ |
$argv[1], $argv |
cmd1 && cmd2 |
cmd1 && cmd2 (or cmd1; and cmd2) |
alias ll='ls -l' in .bashrc |
abbr -a ll ls -l (expands as you type) |
Every variable is a list, and variables are never word-split, so the quoting problems that dominate bash scripting mostly disappear.
Configuration¶
~/.config/fish/config.fish # runs for every fish; wrap interactive-only bits in: if status is-interactive
~/.config/fish/conf.d/*.fish # snippets sourced before config.fish
~/.config/fish/functions/name.fish # one function per file, autoloaded on first use
fish_add_path ~/.cargo/bin # add to PATH once, persistently, without duplicates
set -U EDITOR vim # universal variable: shared by all sessions, survives restarts
funcsave mkcd # save a function defined at the prompt to functions/
fish_config # browser-based UI for colours, prompts, and bindings
Fisher is the usual plugin manager.
Caveats¶
fish can't run bash scripts or source bash files. Run them with bash script.sh, and keep every script's shebang pointing at sh or bash. Tools that print export VAR=... lines for you to eval (ssh-agent, many version managers, cloud CLIs) usually have a fish mode, such as ssh-agent -c or a --shell fish flag. If one doesn't, the bass plugin can import a bash script's environment changes.
Some people prefer to launch fish from their terminal emulator instead of making it their login shell, so login sessions, SSH, and system tools that assume a POSIX login shell keep working. Setting it with chsh works too; just check that $PATH is still complete afterwards.
Further reading¶
- fish documentation and its tutorial
- bash for comparison