knowledge/technology/applications/cli/fish.md
2025-06-20 02:14:30 +02:00

10 KiB
Raw Permalink Blame History

obj website repo
application https://fishshell.com https://github.com/fish-shell/fish-shell

fish

fish is a smart and user-friendly command line shell for Linux, macOS, and the rest of the family.

Usage

Abbreviations

To avoid needless typing, a frequently-run command like git checkout can be abbreviated to gco using the abbr command.

abbr -a gco git checkout

After entering gco and pressing space or enter, a gco in command position will turn into git checkout in the command line. If you want to use a literal gco sometimes, use ctrl-space.

Abbreviations are a lot more powerful than just replacing literal strings. For example you can make going up a number of directories easier with this:

function multicd
    echo cd (string repeat -n (math (string length -- $argv[1]) - 1) ../)
end
abbr --add dotdot --regex '^\.\.+$' --function multicd

Now, .. transforms to cd ../, while ... turns into cd ../../ and .... expands to cd ../../../.

The advantage over aliases is that you can see the actual command before using it, add to it or change it, and the actual command will be stored in history.

Custom bindings

In addition to the standard bindings listed here, you can also define your own with bind:

# Just clear the commandline on control-c
bind ctrl-c 'commandline -r ""'

Put bind statements into config.fish or a function called fish_user_key_bindings.

If you change your mind on a binding and want to go back to fishs default, you can simply erase it again:

bind --erase ctrl-c

History

After a command has been executed, it is remembered in the history list. Any duplicate history items are automatically removed. By pressing the up and down keys, you can search forwards and backwards in the history. If the current command line is not empty when starting a history search, only the commands containing the string entered into the command line are shown.

By pressing alt-up () and alt-down (), a history search is also performed, but instead of searching for a complete commandline, each commandline is broken into separate elements just like it would be before execution, and the history is searched for an element matching that under the cursor.

For more complicated searches, you can press ctrl-r to open a pager that allows you to search the history. It shows a limited number of entries in one page, press ctrl-r again to move to the next page and ctrl-s to move to the previous page. You can change the text to refine your search.

History searches are case-insensitive unless the search string contains an uppercase character. You can stop a search to edit your search string by pressing escape or pagedown.

Prefixing the commandline with a space will prevent the entire line from being stored in the history. It will still be available for recall until the next command is executed, but will not be stored on disk. This is to allow you to fix misspellings and such.

The command history is stored in the file ~/.local/share/fish/fish_history (or $XDG_DATA_HOME/fish/fish_history if that variable is set) by default. However, you can set the fish_history environment variable to change the name of the history session (resulting in a <session>_history file); both before starting the shell and while the shell is running.

See the history command for other manipulations.

Private mode

Fish has a private mode, in which command history will not be written to the history file on disk. To enable it, either set $fish_private_mode to a non-empty value, or launch with fish --private (or fish -P for short).

If you launch fish with -P, it both hides old history and prevents writing history to disk. This is useful to avoid leaking personal information (e.g. for screencasts) or when dealing with sensitive information.

You can query the variable fish_private_mode (if test -n "$fish_private_mode" ...) if you would like to respect the users wish for privacy and alter the behavior of your own fish scripts.

Directory History

Navigating directories is usually done with the cd command, but fish offers some advanced features as well.

The current working directory can be displayed with the pwd command, or the $PWD special variable. Usually your prompt already does this.

Fish automatically keeps a trail of the recent visited directories with cd by storing this history in the dirprev and dirnext variables.

Several commands are provided to interact with this directory history:

  • dirh prints the history
  • cdh displays a prompt to quickly navigate the history
  • prevd moves backward through the history. It is bound to alt-left ()
  • nextd moves forward through the history. It is bound to alt-right ()

Configuration

To store configuration write it to a file called ~/.config/fish/config.fish.

.fish scripts in ~/.config/fish/conf.d/ are also automatically executed before config.fish.

These files are read on the startup of every shell, whether interactive and/or if theyre login shells. Use status --is-interactive and status --is-login to do things only in interactive/login shells, respectively.

Shell Scripting

Shebang

Because shell scripts are written in many different languages, they need to carry information about which interpreter should be used to execute them. For this, they are expected to have a first line, the shebang line, which names the interpreter executable.

A script written in bash would need a first line like this:

#!/bin/bash

When the shell tells the kernel to execute the file, it will use the interpreter /bin/bash.

For a script written in another language, just replace /bin/bash with the interpreter for that language. For example: /usr/bin/python for a python script, or /usr/local/bin/fish for a fish script, if that is where you have them installed.

If you want to share your script with others, you might want to use env to allow for the interpreter to be installed in other locations. For example:

#!/usr/bin/env fish
echo Hello from fish $version

This will call env, which then goes through PATH to find a program called “fish”. This makes it work, whether fish is installed in (for example) /usr/local/bin/fish/usr/bin/fish, or ~/.local/bin/fish, as long as that directory is in PATH.

The shebang line is only used when scripts are executed without specifying the interpreter. For functions inside fish or when executing a script with fish /path/to/script, a shebang is not required (but it doesnt hurt!).

When executing files without an interpreter, fish, like other shells, tries your system shell, typically /bin/sh. This is needed because some scripts are shipped without a shebang line.

Variables

In Fish, variables are assigned using the set command:

set name "Alice"
set -g global_var "I'm global"
set -x PATH $PATH /custom/bin  # export variable to child processes
  • -g: Sets a global variable.
  • -x: Exports the variable.
  • Arrays are space-separated:
set colors red green blue
echo $colors[1]  # Outputs: red

Conditionals

Fish uses if, else if, and else:

set age 20

if test $age -ge 18
    echo "You're an adult"
else if test $age -ge 13
    echo "You're a teenager"
else
    echo "You're a child"
end

Switch Statements

Use switch for cleaner branching with string values:

set lang "rust"

switch $lang
    case rust
        echo "You're using Rust!"
    case python
        echo "Python is cool too."
    case '*'
        echo "Unknown language"
end

Loops

for Loop

for color in red green blue
    echo $color
end

while Loop

set count 1
while test $count -le 3
    echo "Count: $count"
    set count (math $count + 1)
end

Functions

Define reusable blocks of code with function:

function greet
    echo "Hello, $argv"
end

greet "World"  # Output: Hello, World
  • $argv holds all passed arguments.
  • $argv[1] is the first argument.

Returning Values

Functions can return more than just a status code. They can return actual output:

function get_username
    echo "alice"
end

set user (get_username)
echo $user  # Outputs: alice

For status codes, use return:

function is_even
    if test (math "$argv[1] % 2") -eq 0
        return 0
    else
        return 1
    end
end

Events

Fish supports event-driven scripting using functions --on-event:

function notify_start --on-event fish_prompt
    echo "Shell is ready!"
end

Events can be fired with the emit command, and do not have to be defined before. The names just need to match. For example:

function handler --on-event imdone
    echo generator is done $argv
end

function generator
    sleep 1
    # The "imdone" is the name of the event
    # the rest is the arguments to pass to the handler
    emit imdone with $argv
end

Tools

Builtins to do a task, like

  • cd to change the current directory.
  • echo or printf to produce output.
  • set_color to colorize output.
  • set to set, query or erase variables.
  • read to read input.
  • string for string manipulation.
  • path for filtering paths and handling their components.
  • math does arithmetic.
  • argparse to make arguments easier to handle.
  • count to count arguments.
  • type to find out what sort of thing (command, builtin or function) fish would call, or if it exists at all.
  • test checks conditions like if a file exists or a string is empty.
  • contains to see if a list contains an entry.
  • eval and source to run fish code from a string or file.
  • status to get shell information, like whether its interactive or a login shell, or which file it is currently running.
  • abbr manages Abbreviations.
  • bind to change bindings.
  • complete manages completions.
  • commandline to get or change the commandline contents.
  • fish_config to easily change fishs configuration, like the prompt or colorscheme.
  • random to generate random numbers or pick from a list.