Fish Shell Cheatsheet - Config, Functions & Aliases

Fish ships with highlighting, completion and fuzzy history search enabled out of the box, so the gap to Bash is less about features and more about zero setup. The real cost of switching is syntax. Blocks close with end instead of fi or done, variables are set rather than exported, list indexing starts at 1, and string handling leans on command substitution where Bash uses pipes. Interaction is mostly muscle memory, but scripts are where migrations break, because .bashrc fragments copied verbatim tend to fail in confusing ways. This sheet is organised as a translation table: variable scope, control flow, functions and abbreviations, string and pipeline idioms, configuration and themes, then history and key bindings. Use it to convert a Bash habit into its Fish equivalent instead of guessing, or to decide when wrapping the command in sh -c is the saner option. Anything with a shebang and strict POSIX assumptions still belongs in /bin/sh rather than being ported, so judge per script instead of per shell.

SysOps·36 commands·Last updated 2026-07-21

Typical Use Case

For developers who switched to fish and kept writing bash. fish is not a bash with different prompt settings: variables are set with a command rather than an assignment, there is no process substitution, and lists splice instead of word-splitting. Migrating by copying your .bashrc mostly fails. This sheet covers the syntax differences that actually bite, the three variable scopes, history and search, string manipulation, and plugin installation with fisher.

Variables & Scope 7

set name "John"
Set a variable
echo $name
Use a variable
set -x PATH /usr/local/bin $PATH
Set an env var (export)
set -U fish_user_paths /usr/local/bin $fish_user_paths
Set a universal var (persistent)
set -l local_var "value"
Local var (current function only)
set -g global_var "value"
Global variable
set -e name
Erase a variable

Control Flow 5

if test -f file.txt\n echo "exists"\nend
Conditional (file exists)
if test $count -gt 5\n echo "many"\nelse\n echo "few"\nend
if-else branch
for file in *.txt\n echo $file\nend
Loop over files
while read -l line\n echo $line\nend < file.txt
Read a file line by line
switch $status\n case 0\n echo "success"\n case "*"\n echo "failed"\nend
Multi-branch switch

Functions & Aliases 7

function greet\n echo "Hello $argv"\nend
Define a function ($argv args)
funcsave greet
Save a function to the config
functions
List all functions
functions greet
Show a function definition
alias cls="clear"
Create an alias
abbr -a gco "git checkout"
Create an abbreviation (auto-expands)
funced greet
Edit a function interactively

Strings & Pipes 6

string length "hello"
Get string length
string split "," "a,b,c"
Split a string by delimiter
string join ";" "a" "b" "c"
Join strings with a delimiter
string replace "old" "new" "hello old"
Replace a substring
string match -r "^\d+" "123abc"
Regex match
echo "data" | string upper
Pipe to uppercase

Config & Theme 6

fish_config
Configure theme/prompt via web UI
fish_config theme choose Nord
Switch theme from CLI
set -U fish_greeting ""
Disable the greeting
fish_update_completions
Update completion scripts
cat ~/.config/fish/config.fish
View the config file
fisher install jorgebucaran/autopair.fish
Install a plugin with fisher

History & Keys 5

history
View command history
history search "git"
Search history
history delete "rm -rf"
Delete a history entry
Ctrl+R
Incremental history search
Alt+← / Alt+→
Move cursor by word

Parameter matrix

ParameterEffectExample
set -x / -g / -U / -l / -eExported (environment), global, universal (persistent across sessions), local, and eraseset -U fish_user_paths /usr/local/bin $fish_user_paths
fish_greetingThe login banner; setting it to an empty string removes the greetingset -U fish_greeting ""
abbr -aCommand abbreviation: expands on typing and stays out of history, better than alias for commandsabbr -a gco "git checkout"
stringBuilt-in string toolkit that replaces bash cut/sed/tr/grep pipelinesstring split "," "a,b,c"
funcsavePersist a function you just defined interactively into the config filefuncsave greet

Common pitfalls

SymptomMoving export PATH=... from .bashrc into fish leaves PATH unchanged, or adds a literal string to it.

Causeexport VAR=value is not valid fish syntax; fish assigns with the set command and flags.

FixUse set -x PATH /usr/local/bin $PATH, or set -U fish_user_paths /usr/local/bin $fish_user_paths to make it persistent.

SymptomA function you defined interactively is gone after closing the terminal.

Causefish never writes interactively defined functions back to the config file on its own.

FixRun funcsave greet after defining it; edit an existing one with funced greet.

SymptomStartup reports "fish: Unknown command: [" followed by a pile of syntax errors.

CauseBash [ ] conditional syntax was pasted in; fish uses test plus end blocks instead.

FixRewrite as native fish blocks such as if test $count -gt 5 / else / end, converting piece by piece.

SymptomDisabling an alias with "alias name" fails saying the function does not exist.

Causefish aliases are functions, so unalias has no equivalent and removal uses functions -e.

FixUse functions -e name; for command substitutions prefer abbr -a, which expands more cleanly.

SymptomA PATH entry you added manually disappears after restarting the terminal.

Causeset -x only affects the exported variable for this session and never reaches persistent storage.

FixUse set -U fish_user_paths /usr/local/bin $fish_user_paths so it survives across sessions.

Troubleshooting

  1. 1Check whether the config actually took effect

    cat ~/.config/fish/config.fish

    fish reads only this file (not .bashrc); reload with source ~/.config/fish/config.fish after editing.

  2. 2Find a long command you ran before

    history search "git"

    The non-interactive twin of Ctrl+R incremental search; history delete "rm -rf" removes risky entries.

  3. 3Inspect a function or alias definition

    functions && functions greet

    functions lists everything, functions greet prints one definition - enough to spot a shadowed name.

  4. 4Install a plugin

    fisher install jorgebucaran/autopair.fish

    fisher is the fish plugin manager; afterwards run functions to confirm the plugin loaded.

Tips

  • Fish's if/for/function/switch end with end (no fi/done/esac).
  • set -U sets a universal var, persisted and shared across sessions.
  • Fish has autocompletion out of the box; Tab to use, Alt+Right to accept.
  • fisher is Fish's plugin manager: curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher.
  • abbr is lighter than alias and auto-expands to the full command.
  • Fish isn't POSIX-compatible; can't run bash scripts directly, but is more intuitive.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us