Useful tips for working with the fish shell. Fish syntax differs from bash/zsh in several key ways — no export, different variable scoping, and cleaner loop/conditional syntax. For the full reference, see the official fish documentation.
Use fish_add_path to add a directory. It updates fish_user_paths persistently and avoids duplicates:
fish_add_path /path/to/add
Use -v to print the underlying set command that is executed, and verify the result:
fish_add_path -v /path/to/add
echo $fish_user_paths | tr " " "\n" | nl
echo $fish_user_paths | tr " " "\n" | nl
Use contains -i to look up the index of the entry you want to remove and erase it in one step:
set --erase --universal fish_user_paths[(contains -i /path/to/remove $fish_user_paths)]
This avoids the two-step process of listing entries and manually entering the index number.
fish_user_paths is a universal variable, meaning changes persist across all fish sessions automatically.
Fish uses set instead of export. The scope flag controls persistence:
# Session-only (lost when shell exits) — equivalent to bash's plain assignment
set MY_VAR value
# Export to child processes (equivalent to bash's export)
set -x MY_VAR value
# Persist across sessions and all fish windows (universal)
set -Ux MY_VAR value
set -e MY_VAR
-U) are stored in ~/.config/fish/fish_variables and shared across all fish sessions — no need to put them in config.fish.
Fish uses for … in … end instead of bash's do … done:
for f in *.txt
echo $f
end
Loop over a sequence of numbers with seq:
for i in (seq 1 5)
echo $i
end
while loops follow the same end pattern:
while test -f lockfile
sleep 1
end
Fish uses if … else … end — no brackets required, just a command whose exit status is tested:
if test -d ~/projects
echo "projects dir exists"
else
echo "missing"
end
String comparison uses = (one equals sign) inside test, or the string builtin:
if test "$STATUS" = "ok"
echo "all good"
end
and / or replace && / || as statement combiners:
test -f config.yaml; and echo "found" ; or echo "not found"
[[ ]] or [ ] for tests — use the test builtin or string for string operations.
The easiest way to pick a prompt theme is the built-in web UI:
fish_config
This opens a browser tab where you can preview and select prompt themes, colors, and more — no config editing required.
For full control, define a fish_prompt function. Fish looks for it in ~/.config/fish/functions/fish_prompt.fish:
# ~/.config/fish/functions/fish_prompt.fish
function fish_prompt
set_color cyan
echo -n (prompt_pwd) # abbreviated current directory
set_color normal
echo -n ' > '
end
Useful builtins for prompt building:
prompt_pwd — current directory, shortened (respects fish_prompt_pwd_dir_length)set_color — set foreground/background color; set_color normal resetsfish_git_prompt — git branch and status string (enable with set -g __fish_git_prompt_showdirtystate 1)fish_right_prompt function populates the right side of the prompt line — handy for git status or timestamps without cluttering the left.
curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish
Then browse and install themes (omf install bobthefish is a popular choice) or plugins (omf install z for directory jumping). Run omf help to see available commands, or visit the omf repository for a full list of packages.