The Fish Cookbook
This document is a living book of recipes to solve specific programming problems using the fish-shell.
Licensed CC BY-NC-SA 4.0
What's in the menu?
- Setup
- Getting started
- Configuration
- Variables
- How do I set environment variables in fish?
- How do I export a variable in fish?
- How do I list all environment variables in fish?
- How do I set the
$PATHpersistently in fish? - How do I remove a path from the
$PATHin fish? - How do I remove a path persistently from the
$PATHin fish? - How do I check if a path exists in the
$PATHin fish?
- Functions
- How do I create a function in fish?
- How do I create a private function in fish?
- Should function names and file names match?
- Can I define more than one function in a file?
- How do I show the definition of a function in fish?
- What's the difference between functions, builtins and commands in fish?
- How do I list the functions defined in fish?
- How do I check if a function exists in fish?
- Arguments
- Aliases
- IO
- Concurrency
Setup
How do I install fish?
You can find directions in the official website or follow the instructions provided here for your OS.
macOS with homebrew
brew update && brew install fishDebian
wget http://download.opensuse.org/repositories/shells:fish:release:2/Debian_8.0/Release.key
apt-key add - < Release.key
echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/2/Debian_8.0/ /' >> /etc/apt/sources.list.d/fish.list
apt-get update
apt-get install fishUbuntu
sudo apt-add-repository ppa:fish-shell/release-2
sudo apt-get update
sudo apt-get install fishCentOS
cd /etc/yum.repos.d/
wget http://download.opensuse.org/repositories/shells:fish:release:2/CentOS_7/shells:fish:release:2.repo
yum install fishFedora
dnf install fishArch Linux
pacman -S fishGentoo
emerge fishFrom source
sudo apt-get -y install git gettext automake autoconf ncurses-dev build-essential libncurses5-dev
git clone -q --depth 1 https://github.com/fish-shell/fish-shell
cd fish-shell
autoreconf && ./configure
make && sudo make installHow do I make fish my default shell?
Once you have installed fish and it's somewhere in your $PATH, e.g. /usr/local/bin, you can make it your default login shell.
echo /usr/local/bin/fish | sudo tee -a /etc/shells
chsh -s /usr/local/bin/fishHow do I find out where fish is installed?
Use which.
which fish
/usr/local/bin/fishGetting started
How do I learn fish?
The best way to learn fish is to dive in the official documentation and tutorial.
Where can I get help?
What's a prompt and what are all these ugly characters?
The prompt is where you type commands and interact with fish. Read more about the UNIX prompt here.
Maybe it looks like this:
jb@mbp ~/C/fish-shell>The tilde ~ is a short way to show the home directory, rather than /users/jb/home, /Users/jb, etc.
The @ is an arbitrary character I like to use to separate my username jb and my computer's name mbp. You can learn to customize your prompt to show only what matters to you.
The forward slash / is the path delimiter. At a glance, I can see the current directory is under the home directory, inside Code/fish-shell. I like abbreviated paths, so only C is actually shown.
As of fish >=2.3, you can customize the length of the abbreviated path.
set fish_prompt_pwd_dir_length NUMBERor
set fish_prompt_pwd_dir_length 0if don't want abbreviated paths.
jb@mbp ~/Code/fish-shellThe greater-than symbol > indicates the end of the prompt.
You don't like these conventions? Create your own prompt the way you want it.
How do I find my current location in fish?
You can find out where you are via the read-only environment variable $PWD.
echo $PWD
/Users/jb/Code/fish-shellAnother way to find out the current directory is via the pwd builtin.
pwd
/Users/jb/Code/fish-shellIn fish, both $PWD and pwd always resolve symbolic links. This means that, if you are inside a directory that is a symbolic reference to another, you still get the path to the real directory.
Interactively, pwd is easier to type. For scripting, $PWD is a function call less expensive.
Show me how!
set -l cwd (pwd)
echo "The current working directory is $cwd"
# Versus
echo "The current working directory is $PWD"How do I find and run commands in fish?
To run a command type the name of the command and press return.
lsOr, start typing the command you are looking for, and press tab. Fish will use the builtin pager which you can browse and select the command interactively.
Fish knows what commands are available by looking at the $PATH environment variable. This variable contains a list of paths, and every binary file inside any of those paths can be run by their name.
Print your $PATH contents.
printf "%s\n" $PATH
/usr/local/bin
/usr/bin
/binor list every command in your system and display them in columns.
ls $PATH | columnIf the list is truncated, use:
ls $PATH | column | lessUse k and j to navigate the list down / up, and q to exit.
The $PATH variable is created at the start of the fish process during the environment initialization. You can modify, prepend or append to this variable yourself, e.g., in ~/.config/fish/config.fish.
Similar to the type, builtin and functions builtins previously introduced, *nix systems often include one or more shell-agnostic alternatives, e.g., which, apropos, whatis, etc.
These commands overlap in functionality, but also possess unique features. Consult your system's manpage for details.
How do I check if a command succeeded in fish?
Every command returns an exit code to indicate whether they succeeded or not. An exit code of 0 means success. Anything else means failure. Different commands use different integers to represent what errors can happen.
You can check the exit code of any command using the read-only variable $status.
my_command
echo $statusWhat is the fish shebang?
The shebang is a special comment that tells the shell to run a script with a particular program, e.g., node, python.
To run a script with fish by default, add a shebang to the first line in your file:
#!/usr/bin/env fishShow me how!
#!/usr/bin/env fish
if status --is-interactive
echo "We live in an interactive world!"
endSave that to a file and mark it as executable.
chmod +x my_scriptThe system above allow us to run the script directly by using its path
./my_scriptinstead of
fish my_scriptVariables
How do I set environment variables in fish?
Use the set builtin.
set foo 42The set builtin accepts the following flags to explicitly declare the scope of the variable:
-l,--local: available only to the innermost block-g,--global: available outside blocks and by other functions-U,--universal: shared between all fish sessions and persisted across restarts of the shell-x,--export: available to any child process spawned in the current session
If no scope modifier is used, the variable will be local to the current function; otherwise, it will be global.
If the variable has already been defined, the previous scope will be used.
Local Variables
The variable foo will not be available outside of the if block.
if true
set -l foo 42
end
echo "foo=$foo" # foo=Global Variables
The variable foo will be available outside the if block.
if true
set -g foo 42
end
echo "foo=$foo" # foo=42Universal Variables
The variable foo will be preserved and available to future shell sessions.
set -U foo 42
fish
echo "foo=$foo" # foo=42Exported Variables
The variable foo will be local and exported, therefore available to the fish child process created inside the if block.
if true
set -lx foo 42
fish -c 'echo "foo=$foo"' # foo=42
endThe variable foo will be global, but since it's not exported, it won't be available to the fish child process.
set -g foo 42
fish -c 'echo "foo=$foo"' # foo=The variable GPG_AGENT_INFO will be universal and exported, therefore preserved across future shell sessions and child processes.
set -Ux GPG_AGENT_INFO /Users/jb/.gnupg/S.gpg-agent:12345:2How do I export a variable in fish?
Use the set builtin and the scope modifier -x or --export.
set -x foo 42
fish -c 'echo "foo=$foo"' # foo=42How do I list all environment variables in fish?
Use the set builtin without any modifier flags.
setTo print only the variable names, without the values, use --name.
set --namesTo not truncate long lines use --long.
set --longHow do I set the $PATH persistently in fish?
The correct way to persistently add a path to your $PATH is using fish $fish_user_paths variable.
set -U fish_user_paths $fish_user_paths my_pathSee
$PATHin the fish tutorial for more information.
How do I remove a path from the $PATH in fish?
Use the set builtin with the -e or --erase flag in combination with the contains builtin to find the index of the path you want to remove.
if set -l index (contains -i $my_path $PATH)
set -e PATH[$index]
endHow do I remove a path persistently from the $PATH in fish?
Use the set builtin with the -e or --erase flag in combination with the contains builtin to find the index of the path you want to remove.
if set -l index (contains -i $my_path $fish_user_paths)
set -e -U fish_user_paths[$index]
endHow do I check if a path exists in the $PATH in fish?
Use the contains builtin.
if contains $my_path $PATH
# $my_path is in $PATH
endFunctions
How do I create a function in fish?
Use the function builtin.
function mkdirp
mkdir -p $argv
endTo make this function available in future fish sessions save it to ~/.config/fish/functions/mkdirp.fish. A clean way to accomplish this is using the funcsave function.
funcsave mkdirpAlternatively, you can use the functions builtin to write the function definition to a file.
functions mkdirp > ~/.config/fish/functions/mkdirp.fishHow do I create a private function in fish?
You can't. In fish, functions are always public.
As a workaround, use a custom namespace to prefix any function you want to treat as private.
function _prefix_my_function
endIt's not impossible to simulate private scope using functions -e.
Show me how!
function foo
function _foo
echo Foo
functions -e _foo # Erase _foo
end
_foo
endShould function names and file names match?
Yes. The lazy-loading / autoloading mechanism relies on this convention to work.
If you have a file ~/.config/fish/functions/foo.fish with a valid function definition bar:
- In a new shell, trying to run
barproduces an unknown-command error. - Typing
foowill highlight as a valid command, but produce an unknown-command error. - Trying to run
baragain now works as intended.
Show me how!
Save bar to ~/.config/fish/functions/foo.fish.
function bar
echo Bar
end
functions bar > ~/.config/fish/functions/foo.fishCreate a new shell session.
fish
Try to run bar, then foo, then bar again.
bar
# fish: Unknown command 'bar'
foo
# fish: Unknown command 'foo'
bar
# Bar
Can I define more than one function in a file?
Yes, you can. Note that fish does not have private functions, so every function in the file ends up in the global scope when the file is loaded. Functions are eagerly loaded as well, which it's not as effective as using one function per file.
How do I show the definition of a function in fish?
If you know the command is a function, use the functions builtin.
functions my_functionIf you are not sure whether the command is a function, a builtin or a system command, use type.
type fish
fish is /usr/local/bin/fishWhat's the difference between functions, builtins, and commands in fish?
System commands are executable scripts, binaries or symbolic links to binaries present in your $PATH variable. A command runs as a child process and has only access to environment variables which have been exported. Example: fish.
Functions are user-defined. Some functions are included with your fish distribution. Example: alias, type, nextd.
Builtins are commands compiled with the fish executable. Builtins have access to the environment, so they behave like functions. Builtins do not spawn a child process. Example: functions.
How do I list the functions defined in fish?
Use the functions builtin without arguments.
The list will omit functions whose name start with an underscore. Functions that start with an underscore are often called hidden. To show everything, use functions -a or functions --all.
Alternatively, launch the fish Web-based configuration and navigate to the /functions tab.
fish_config functions
How do I check if a function exists in fish?
Use the type function to query information about commands, builtins or functions.
if not type --quiet "$command_name"
exit 1
endUse builtin --names to query builtins.
if not contains -- "$command_name" (builtin --names)
exit 1
endUse functions --query to check if a function exists.
if not functions --query "$command_name"
exit 1
endUse command --search for other commands.
if not command --search "$command_name" > /dev/null
exit 1
endEasier in fish >= 2.5
if not command --search --quiet "$command_name"
exit 1
endArguments
How do I access the arguments passed to a function in fish?
Use the $argv variable.
function Foo
printf "%s\n" $argv
end
Foo foo bar baz
foo
bar
bazHow do I access the arguments passed to a script in fish?
Use the $argv variable. Pass the arguments when running the script.
fish ./my_script foo bar baz
foo
bar
bazExample: my_script
#!/usr/bin/env fish
printf "%s\n" $argvHow do I parse command line arguments in fish?
Use a for loop.
for option in $argv
switch "$option"
case -f --foo
case -b --bar
case \*
printf "error: Unknown option %s\n" $option
end
endFor a more complete CLI parsing solution, see getopts.
Aliases
How do I define an alias in fish?
Create a function and save it to ~/.config/fish/functions.
function rimraf
rm -rf $argv
endFor backward compatibility with POSIX shells, use the alias function.
alias rimraf "rm -rf"Avoid using alias inside ~/.config/fish/config.fish.
What's wrong with aliases?
Aliases created with alias will not be available in new shell sessions. If that's the behavior you need, then alias is acceptable for interactive use.
To persist aliases across shell sessions, create a function and save it to ~/.config/fish/functions. This takes advantage of fish function lazy-loading / autoloading mechanism.
Using alias inside ~/.config/fish/config.fish will slow down your shell start as each alias/function will be eagerly loaded.
Configuration
Where's the .bash_profile or .bashrc equivalent in fish?
Your fish configuration is saved to ~/.config/fish/config.fish.
IO
How do I read from a file in fish?
To read a file line by line, use the read builtin.
while read -la line
echo $line
end < my_fileHow do I read from stdin in fish?
Use the read builtin.
read --prompt "echo 'Name: ' " -l nameName: Marvin
echo $name
MarvinTo read from an arbitrary input stream use read together with the while builtin.
while read -la line
echo $line
endHow do I redirect stdout or stderr to a file in fish?
Redirect stderr to $my_file.
my_command 2> $my_fileRedirect stdout to $my_file.
my_command > $my_fileRedirect stdout to stderr.
my_command >&2Redirect stderr to stdout.
my_command 2>&1Concurrency
How do I run a command in the background in fish?
Use &.
sleep 10 &See also Background jobs in the fish documentation.
How do I check if background jobs are running in fish?
Use the jobs builtin.
if jobs > /dev/null
echo Busy
endHow do I synchronize two or more background tasks in fish?
Since fish 3.0.0 there is a dedicated wait, but you can write your own if you are using an older fish.
First, to check if tasks are running in the background, parse the output from the jobs builtin.
Parse by Job ID
function get_jobs
jobs $argv | command awk -v FS=\t '
/[0-9]+\t/{
jobs[++nJobs] = $1
}
END {
for (i in jobs) {
print(jobs[i])
}
exit nJobs == 0
}
'
endParse by Group ID
function get_jobs
jobs -g | command awk 'NR > 0 { print; i++ } END { exit i == 0 }'
endThen, block the foreground until all background jobs are finished.
function wait
while true
set -l has_jobs
set -l all_jobs (get_jobs)
or break
for j in $argv
if contains -- $j $all_jobs
set -e has_jobs
break
end
end
if set -q has_jobs
break
end
end
endShow me how!
set -l urls "https://"{google,twitter,youtube,facebook,github}".com"
for url in $urls
fish -c "curl -Lw \"$url: %{time_total}s\n\" -o /dev/null -s $url" &
end
wait (get_jobs)How do I wait for a background process in fish?
Since fish 3.0.0 there is a dedicated wait builtin to wait for a background process. For older fish distributions without wait, use the technique described in How do I synchronize two or more background tasks in fish?.