Fish shell configuration file
Table of Contents
- ~/.config/fish/config.fish
- ~/config/fish/conf.d
- 00-distro.fish
- 00-locale.fish
- 01-xdg_base_directory.fish
- 02-editors.fish
- 03-nix.fish
- 04-terminals.fish
- 05-filemanagers.fish
- 06-security.fish
- 07-browsers.fish
- 08-mail_apps.fish
- 99-doom_emacs.fish
- 99-composer.fish
- 99-screen.fish
- 99-fsharp.fish
- 99-elinks.fish
- 99-ccache.fish
- 99-ghcup.fish
- 99-cargo.fish
- 99-ruby.fish
- 99-go.fish
- 99-virtualenvwrapper.fish
- 99-python.fish
- 99-nano.fish
- 99-emacs_vterm.fish
- ~/.config/fish/functions
This is my Fish shell configuration, as always, nothing fancy.
~/.config/fish/config.fish
From all the shells I've used, this is the most recent. And it is intentionally not fully POSIX compliant, meaning porting POSIX compliant script may require significant rewrite.
As for now, just to conditionals for when called as a login shell or when called interactively. Most people used fish as an interactive shell only, meaning the default shell (usually bash) is still used for initializing. But when I used fish I set it up as my default shell (using chsh).
to check if you're in interactive or login shell, just run the following snippet:
status --is-interactive || echo $status
and check its output
interactive settings
if status is-interactive # Commands to run in interactive sessions can go here [ -d "$HOME/.local/bin" ] && set -x PATH $HOME/.local/bin $PATH # completions for dir in /usr/share/fish/completions \ /usr/share/fish/vendor_completions.d \ /usr/local/share/fish/completions \ ~/.config/fish/completions if [ -d "$dir" ] for files in "$dir/*" [ -f $files ] && source $files end end end end
Login shell
This allow us to run fish as a login shell, while still having all the environment variables I would typically have in bash1.
# source /etc/profile with bash if status is-login exec bash -c "test -e /etc/profile && source /etc/profile;\ exec fish" end
~/config/fish/conf.d
Mostly just ports from my POSIX shell configuration.
00-distro.fish
# ~/.config/fish/conf.d/00-distro.fish # <alexforsale@yahoo.com> if [ -f /etc/os-release ] # freedesktop.org and systemd set -x DISTRO (grep '^ID=' /etc/os-release| sed 's/^ID=//') set -x DISTROVER (grep '^VERSION_ID' /etc/os-release| sed 's/^ID=//') else if [ (command -v lsb_release >/dev/null) ] # linuxbase.org set -x DISTRO (lsb_release -si | awk '{print tolower ($0)}') set -x DISTROVER (lsb_release -sr | awk '{print tolower ($0)}') else if [ -f /etc/lsb-release ] # For some versions of Debian/Ubuntu without lsb_release command set -x DISTRO (grep '^ID' /etc/lsb-release| sed 's/^DISTRIB_ID=//') set -x DISTROVER (grep '^VERSION_ID' /etc/lsb-release| sed 's/^DISTRIB_RELEASE=//') else if [ -f /etc/debian_version ]; then # Older Debian/Ubuntu/etc. set -x DISTRO Debian set -x DISTROVER (cat /etc/debian_version) else set -x DISTRO (uname -s) set -x DISTROVER (uname -r) end
00-locale.fish
# ~/.config/fish/conf.d/00-locale.fish # <alexforsale@yahoo.com> set -x LANG en_US.UTF-8 if [ -z $MM_CHARSET ] set -x MM_CHARSET $LANG end
01-xdg_base_directory.fish
# ~/.config/fish/conf.d/01-xdg_base_directory.fish # <alexforsale@yahoo.com> if [ -z $XDG_CONFIG_HOME ] if [ -d $HOME/.config ] mkdir -p $HOME/.config end set -x XDG_CONFIG_HOME $HOME/.config end if [ -z $XDG_CACHE_HOME ] if [ -d $HOME/.cache ] mkdir -p $HOME/.cache end set -x XDG_CACHE_HOME $HOME/.cache end if [ -z $XDG_DATA_HOME ] if [ -d $HOME/.local/share ] mkdir -p $HOME/.local/share end set -x XDG_DATA_HOME $HOME/.local/share end if [ -z $XDG_DESKTOP_DIR ] set -x XDG_DESKTOP_DIR $HOME/Desktop mkdir -p $XDG_DESKTOP_DIR end if [ -z $XDG_DOWNLOAD_DIR ] set -x XDG_DOWNLOAD_DIR $HOME/Downloads mkdir -p $XDG_DOWNLOAD_DIR end if [ -z $XDG_TEMPLATES_DIR ] set -x XDG_TEMPLATES_DIR $HOME/Templates mkdir -p $XDG_TEMPLATES_DIR end if [ -z $XDG_PUBLICSHARE_DIR ] set -x XDG_PUBLICSHARE_DIR $HOME/Public mkdir -p $XDG_PUBLICSHARE_DIR end if [ -z $XDG_DOCUMENTS_DIR ] set -x XDG_DOCUMENTS_DIR $HOME/Documents mkdir -p $XDG_DOCUMENTS_DIR end if [ -z $XDG_MUSIC_DIR ] set -x XDG_MUSIC_DIR $HOME/Music mkdir -p $XDG_MUSIC_DIR end if [ -z $XDG_PICTURES_DIR ] set -x XDG_PICTURES_DIR $HOME/Pictures mkdir -p $XDG_PICTURES_DIR end if [ -z $XDG_VIDEOS_DIR ] set -x XDG_VIDEOS_DIR $HOME/Videos mkdir -p $XDG_VIDEOS_DIR end if [ -n $GUIX_LOCPATH ] || [ $DISTRO = "guix" ] if [ -d $HOME/.nix-profile/share ] set -x XDG_DATA_DIRS "$XDG_DATA_DIRS:$HOME/.nix-profile/share" end end
02-editors.fish
# ~/.config/fish/conf.d/03-nix.fish # from https://github.com/lilyball/nix-env.fish/blob/master/conf.d/nix-env.fish # <alexforsale@yahoo.com> if [ -f $HOME/.config/chemacs/profile ] && [ -z "$CHEMACS_PROFILE" ] set -x CHEMACS_PROFILE (cat $HOME/.config/chemacs/profile) else if [ -f $HOME/.emacs-profile ] set -x CHEMACS_PROFILE (cat $HOME/.emacs-profile) end if [ -n "$CHEMACS_PROFILE" ] set emacs_args "-s $CHEMACS_PROFILE" end if [ (command -v emacs) ] if [ ! (set -q VISUAL) ] set -x VISUAL "emacsclient -t $emacs_args -a ''" end if [ ! (set -q EDITOR) ] set -x EDITOR "emacsclient -c $emacs_args -a ''" end set -x ALTERNATE_EDITOR $VISUAL else if [ (command -v gvim) ] if [ ! (set -q EDITOR) ] set -x EDITOR gvim end if [ ! (set -q VISUAL) ] end else for editor in nvim leafpad l3afpad kate pluma kwrite scribe geany gedit code vi if [ (command -v $editor ) ] [ ! (set -q EDITOR) ] && set -x EDITOR $editor end set -x VISUAL $EDITOR set -x ALTERNATE_EDITOR $EDITOR end end
03-nix.fish
# ~/.config/fish/conf.d/03-nix.fish # from https://github.com/lilyball/nix-env.fish/blob/master/conf.d/nix-env.fish # <alexforsale@yahoo.com> set -l nix_profile_path /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh set -l single_user_profile_path ~/.nix-profile/etc/profile.d/nix.sh if test -e $nix_profile_path # The path exists. Double-check that this is a multi-user install. # We can't just check for ~/.nix-profile/… because this may be a single-user install running as # the wrong user. # stat is not portable. Splitting the output of ls -nd is reliable on most platforms. set -l owner (string split -n ' ' (command ls -nd /nix/store 2>/dev/null))[3] if not test -k /nix/store -a $owner -eq 0 # /nix/store is either not owned by root or not sticky. Assume single-user. set nix_profile_path $single_user_profile_path end else # The path doesn't exist. Assume single-user set nix_profile_path $single_user_profile_path end if test -e $nix_profile_path # Source the nix setup script # We're going to run the regular Nix profile under bash and then print out a few variables for line in (command env -u BASH_ENV bash -c '. "$0"; for name in PATH "${!NIX_@}"; do printf "%s=%s\0" "$name" "${!name}"; done' $nix_profile_path | string split0) set -xg (string split -m 1 = $line) end # Insert Nix's fish share directories into fish's special variables. # nixpkgs-installed fish tries to set these up already if NIX_PROFILES is defined, which won't # be the case when sourcing $__fish_data_dir/share/config.fish normally, but might be for a # recursive invocation. To guard against that, we'll only insert paths that don't already exit. # Furthermore, for the vendor_conf.d sourcing, we'll use the pre-existing presence of a path in # $fish_function_path to determine whether we want to source the relevant vendor_conf.d folder. # To start, let's locally define NIX_PROFILES if it doesn't already exist. set -al NIX_PROFILES if test (count $NIX_PROFILES) -eq 0 set -a NIX_PROFILES $HOME/.nix-profile end # Replicate the logic from nixpkgs version of $__fish_data_dir/__fish_build_paths.fish. set -l __nix_profile_paths (string split ' ' -- $NIX_PROFILES)[-1..1] set -l __extra_completionsdir \ $__nix_profile_paths/etc/fish/completions \ $__nix_profile_paths/share/fish/vendor_completions.d set -l __extra_functionsdir \ $__nix_profile_paths/etc/fish/functions \ $__nix_profile_paths/share/fish/vendor_functions.d set -l __extra_confdir \ $__nix_profile_paths/etc/fish/conf.d \ $__nix_profile_paths/share/fish/vendor_conf.d ### Configure fish_function_path ### # Remove any of our extra paths that may already exist. # Record the equivalent __extra_confdir path for any function path that exists. set -l existing_conf_paths for path in $__extra_functionsdir if set -l idx (contains --index -- $path $fish_function_path) set -e fish_function_path[$idx] set -a existing_conf_paths $__extra_confdir[(contains --index -- $path $__extra_functionsdir)] end end # Insert the paths before $__fish_data_dir. if set -l idx (contains --index -- $__fish_data_dir/functions $fish_function_path) # Fish has no way to simply insert into the middle of an array. set -l new_path $fish_function_path[1..$idx] set -e new_path[$idx] set -a new_path $__extra_functionsdir set fish_function_path $new_path $fish_function_path[$idx..-1] else set -a fish_function_path $__extra_functionsdir end ### Configure fish_complete_path ### # Remove any of our extra paths that may already exist. for path in $__extra_completionsdir if set -l idx (contains --index -- $path $fish_complete_path) set -e fish_complete_path[$idx] end end # Insert the paths before $__fish_data_dir. if set -l idx (contains --index -- $__fish_data_dir/completions $fish_complete_path) set -l new_path $fish_complete_path[1..$idx] set -e new_path[$idx] set -a new_path $__extra_completionsdir set fish_complete_path $new_path $fish_complete_path[$idx..-1] else set -a fish_complete_path $__extra_completionsdir end ### Source conf directories ### # The built-in directories were already sourced during shell initialization. # Any __extra_confdir that came from $__fish_data_dir/__fish_build_paths.fish was also sourced. # As explained above, we're using the presence of pre-existing paths in $fish_function_path as a # signal that the corresponding conf dir has also already been sourced. # In order to simulate this, we'll run through the same algorithm as found in # $__fish_data_dir/config.fish except we'll avoid sourcing the file if it comes from an # already-sourced location. # Caveats: # * Files will be sourced in a different order than we'd ideally do (because we're coming in # after the fact to source them). # * If there are existing extra conf paths, files in them may have been sourced that should have # been suppressed by paths we're inserting in front. # * Similarly any files in $__fish_data_dir/vendor_conf.d that should have been suppressed won't # have been. set -l sourcelist for file in $__fish_config_dir/conf.d/*.fish $__fish_sysconf_dir/conf.d/*.fish # We know these paths were sourced already. Just record them. set -l basename (string replace -r '^.*/' '' -- $file) contains -- $basename $sourcelist or set -a sourcelist $basename end for root in $__extra_confdir for file in $root/*.fish set -l basename (string replace -r '^.*/' '' -- $file) contains -- $basename $sourcelist and continue set -a sourcelist $basename contains -- $root $existing_conf_paths and continue # this is a pre-existing path, it will have been sourced already [ -f $file -a -r $file ] and source $file end end end
04-terminals.fish
# ~/.config/fish/conf.d/04-terminals.fish # ${TERMINAL} apps # <alexforsale@yahoo.com> for term in alacritty urxvt xterm gnome-terminal terminator tilda guake konsole lxterminal yakuake st terminology xfce4-terminal if [ (command -v $term) ] [ -z $TERMINAL ] && set -x TERMINAL $term end end
05-filemanagers.fish
# ~/.config/fish/conf.d/05-filemanagers.sh # filemanager configuration # <alexforsale@yahoo.com> for file in ranger pcmanfm thunar caja nautilus nemo dolphin rox if [ (command -v $file) ] [ -z $FILE ] && set -e FILE $file end end
06-security.fish
# ~/.config/fish/conf.d/06-security.fish # doom emacs path # <alexforsale@yahoo.com> set -x GPG_TTY (tty) # https://wiki.gentoo.org/wiki/GnuPG#Automatically_starting_the_GPG_agent if [ -n $SSH_CONNECTION ] set -x PINENTRY_USER_DATA "USE_CURSES=1" end switch $DISTRO case freebsd for i in curses gnome3 gtk2 qt5 if [ (command -v pinentry-$i) ] set _PINENTRY (command -v pinentry-$i) else set _PINENTRY (command -v pinentry) end end case '*' if [ -z "(pgrep -u $USER -x gpg-agent)" ] gpg-agent --pinentry-program (command -v pinentry) >/dev/null end end
07-browsers.fish
# ~/.config/fish/conf.d/07-browser.fish # ${BROWSER} configuration # <alexforsale@yahoo.com> # Browser for browser in firefox google-chrome-stable midori qutebrowser chromium seamonkey falkon if [ (command -v browser) ] [ -z $BROWSER ] && set -x BROWSER $browser else for term_browser in elinks lynx w3m links if [ (command -v term_browser) ] [ -z $BROWSER ] && set -x BROWSER $term_browser end end end end
08-mail_apps.fish
# ~/.config/fish/conf.d/08-mail_apps.sh # ${MAIL_APP} configuration # <alexforsale@yahoo.com> for mail_app in alpine balsa evolution geary kmail kube mailspring thunderbird if [ (command -v $mail_app) ] [ -z $MAIL_APP ] && set -x MAIL_APP $mail_app end end
99-doom_emacs.fish
# ~/.config/fish/conf.d/99-doom_emacs.sh # doom emacs path # <alexforsale@yahoo.com> if [ (command -v emacs) ] && [ -e $HOME/.emacs.d/bin/doom ] [ ! (command -v doom) ] && set -x PATH $PATH $HOME/.emacs.d/bin else if [ -d $HOME/.config/doom/bin ] [ ! (command -v doom) ] set -x PATH $PATH $HOME/.config/doom/bin end
99-composer.fish
# ~/.config/fish/conf.d/99-composer.fish # <alexforsale@yahoo.com> if command -v composer &>/dev/null and test -d $HOME/.config/composer/vendor/bin set -x PATH $PATH $HOME/.config/composer/vendor/bin end
99-screen.fish
# ~/.config/fish/conf.d/99-screen.fish # configuration for GNU Screen # <alexforsale@yahoo.com> if [ (command -v screen) ] && [ ! -d $XDG_CONFIG_HOME/screen ] if [ -d $HOME/.screen ] mv $HOME/.screen $XDG_CONFIG_HOME/screen else mkdir -p $XDG_CONFIG_HOME/screen end end set -x SCREENDIR $XDG_CONFIG_HOME/screen chmod 700 $SCREENDIR set -x SCREENRC $SCREENDIR/config
99-fsharp.fish
# ~/.config/fish/conf.d/99-fsharp.fish # configuration for F# # <alexforsale@yahoo.com> if [ (command -v dotnet) ] && [ -d $HOME/.dotnet/tools ] set -x PATH $PATH $HOME/.dotnet/tools end
99-elinks.fish
# ~/.config/fish/conf.d/99-elinks.fish # configuration for elinks # <alexforsale@yahoo.com> if [ (command -v elinks) ] && [ -d $XDG_CONFIG_HOME/elinks ] && [ -d $HOME/.elinks ] mv $HOME/.elinks $XDG_CONFIG_HOME/elinks else mkdir -p $XDG_CONFIG_HOME/elinks end set -x ELINKS_CONFDIR $XDG_CONFIG_HOME/elinks
99-ccache.fish
# ~/.config/fish/conf.d/99-ccache.fish # configuration for ccache # <alexforsale@yahoo.com> if [ (command -v ccache) ] && [ -d /usr/lib/ccache/bin ] set -x PATH /usr/lib/ccache/bin $PATH end switch $DISTRO case gentoo [ -r /var/cache/ccache ] && set -x CCACHE_DIR /var/cache/ccache end
99-ghcup.fish
# ~/.config/fish/conf.d/99-ghcup.fish # ghcup configuration # <alexforsale@yahoo.com> if [ (command -v ghcup) ] [ -d $HOME/.cabal/bin ] && set -x PATH $HOME/.cabal/bin $PATH [ -d $HOME/.ghcup/bin ] && set -x PATH $HOME/.ghcup/bin $PATH end
99-cargo.fish
# ~/.config/fish/conf.d/99-cargo.fish # cargo configuration # https://wiki.archlinux.org/index.php/Rust#Cargo # <alexforsale@yahoo.com> if [ -d $HOME/.cargo/bin ] set -x PATH $HOME/.cargo/bin $PATH end
99-ruby.fish
# ~/.config/fish/conf.d/99-ruby.fish # ruby configuration # <alexforsale@yahoo.com> if [ (command -v ruby) ] && [ -d (ruby -e 'print Gem.user_dir')/bin ] set -x PATH (ruby -e 'print Gem.user_dir')/bin $PATH end
99-go.fish
# ~/.config/fish/conf.d/99-go.fish # $GOPATH configuration # <alexforsale@yahoo.com> if [ (command -v go) ] # set GOPATH to ~/.local so we don't need # to add more PATH set -x GOPATH $HOME/.local end
99-virtualenvwrapper.fish
# ~/.config/fish/conf.d/99-virtualenvwrapper.fish # $GOPATH configuration # <alexforsale@yahoo.com> [ -d $HOME/.virtualenvs ] && set -x WORKON_HOME $HOME/.virtualenvs [ -x /usr/bin/virtualenvwrapper.sh ] && exec bash -c "source /usr/bin/virtualenvwrapper.sh"
99-python.fish
# ~/.config/fish/conf.d/99-python.fish # Python configuration # <alexforsale@yahoo.com> [ $(command -v pipenv) ] && set -x PIPENV_VENV_IN_PROJECT 1 if [ $(command -v pyenv) ] set -Ux PYENV_ROOT $HOME/.pyenv fish_add_path $PYENV_ROOT/bin pyenv init - | source end
99-nano.fish
# ~/.config/fish/conf.d/99-nano.fish # GNU Nano configuration # <alexforsale@yahoo.com> if [ $(command -v nano) ] if [ ! -d $XDG_CONFIG_HOME/nano ] mkdir -p $XDG_CONFIG_HOME/nano if [ -f $HOME/.nanorc ] if [ ! -f $XDG_CONFIG_HOME/nano/nanorc ] mv $HOME/.nanorc $XDG_CONFIG_HOME/nano/nanorc else mv $HOME/.nanorc $XDG_CONFIG_HOME/nano/nanorc.bak end end [ ! -d $XDG_CONFIG_HOME/nano/backups ] && mkdir -pv $XDG_CONFIG_HOME/nano/backups end end
99-emacs_vterm.fish
# ~/.config/fish/conf.d/99-emacs-vterm.sh # https://github.com/akermu/emacs-libvterm # <alexforsale@yahoo.com> function vterm_printf if begin [ -n "$TMUX" ]; and string match -q -r "screen|tmux" "$TERM" end # tell tmux to pass the escape sequences through printf "\ePtmux;\e\e]%s\007\e\\" "$argv" else if string match -q -- "screen*" "$TERM" # GNU screen (screen, screen-256color, screen-256color-bce) printf "\eP\e]%s\007\e\\" "$argv" else printf "\e]%s\e\\" "$argv" end end if [ "$INSIDE_EMACS" = vterm ] function clear vterm_printf "51;Evterm-clear-scrollback" tput clear end end function fish_title hostname echo ":" prompt_pwd end function vterm_prompt_end vterm_printf '51;A'(whoami)'@'(hostname)':'(pwd) end functions --copy fish_prompt vterm_old_fish_prompt function fish_prompt --description 'Write out the prompt; do not replace this. Instead, put this at end of your file.' # Remove the trailing newline from the original prompt. This is done # using the string builtin from fish, but to make sure any escape codes # are correctly interpreted, use %b for printf. printf "%b" (string join "\n" (vterm_old_fish_prompt)) vterm_prompt_end end function vterm_cmd --description 'Run an Emacs command among the ones been defined in vterm-eval-cmds.' set -l vterm_elisp () for arg in $argv set -a vterm_elisp (printf '"%s" ' (string replace -a -r '([\\\\"])' '\\\\\\\\$1' $arg)) end vterm_printf '51;E'(string join '' $vterm_elisp) end function find_file set -q argv[1]; or set argv[1] "." vterm_cmd find-file (realpath "$argv") end function say vterm_cmd message "%s" "$argv" end
~/.config/fish/functions
fish_greeting
function fish_greeting echo "Welcome to "(fish --version)", the friendly interactive shell" echo Type (set_color green; echo -n help) (set_color normal; echo -n "for instructions on how to use fish") end
This is basically the default greetings, the only change is I've added the version.