Skip to content

Instantly share code, notes, and snippets.

@Birdie0
Last active May 7, 2026 12:37
Show Gist options
  • Select an option

  • Save Birdie0/c20ae3a92e01c4db348d2f5270255b0b to your computer and use it in GitHub Desktop.

Select an option

Save Birdie0/c20ae3a92e01c4db348d2f5270255b0b to your computer and use it in GitHub Desktop.
Bash / Zsh / fish config files

bash

Shell modes

Login shell

bash -l or bash --login to run as login shell

if shopt -q login_shell
then
  echo 'login shell'
else
  echo 'non-login shell'
fi

Interactive shell

bash -i to run as interactive shell

if [[ $- == *i* ]]
then
  echo 'interactive shell'
else
  echo 'non-interactive shell'
fi

Startup source order

  • if shell runs in login mode (interactive flag is ignored) and --noprofile is not provided (skips sourcing startup files), next files will be sourced:
    • /etc/profile, which sources next files:
      • /etc/bash.bashrc
      • /etc/profile.d/*.sh
    • checks next files for being present, sources first found only:
      • ~/.bash_profile - usually missing, if present it's recommended them to include source ~/.profile for consistency
      • ~/.bash_login - usually missing, if present it's recommended them to include source ~/.profile for consistency
      • ~/.profile - present by default, sources next file:
        • ~/.bashrc
    • on shell exit sources ~/.bash_logout
  • if shell runs in non-login mode:
    • and interactive and --norc is not provided (skips sourcing startup files), next files will be sourced:
      • /etc/bash.bashrc
      • ~/.bashrc
    • and non-interactive:
      • no files are being sourced

Reference

fish

Shell modes

Login shell

fish -l or fish --login to run as login shell

if status is-login
    echo 'login shell'
else
    echo 'non-login shell'
end

Interactive shell

fish -i or fish --interactive to run as interactive shell

if status is-interactive
    echo 'interactive shell'
else
    echo 'non-interactive shell'
end

Startup source order

Fish uses a lot of directory paths to store its configs:

Config Variable Path
user $__fish_config_dir $XDG_CONFIG_HOME/fish => ~/.config/fish
system-wide $__fish_sysconf_dir /etc/fish
software $__fish_user_data_dir $XDG_DATA_HOME/fish => ~/.local/share/fish
vendor $__fish_data_dir /usr/share/fish (varies)

About vendor config

If $XDG_DATA_DIRS is set, loop through it (path variable), otherwise $__fish_data_dir is used:

  • each {path}/fish/vendor_conf.d is appended to $__fish_vendor_confdirs
  • each {path}/fish/vendor_functions.d is appended to $__fish_vendor_functionsdirs
  • each {path}/fish/vendor_completions.d is appended to $__fish_vendor_completionsdirs

Note: updating $XDG_DATA_DIRS won't update *dirs variables retrospectively.

Startup source order

--no-config/-N skips sourcing all config files.

  • /usr/share/fish/config.fish sources next files, if these any duplicates basenames (/some/path/dir.fish -> dir.fish) only first will be sourced:
    • $__fish_config_dir/conf.d/*.fish
    • $__fish_sysconf_dir/conf.d/*.fish
    • loop through $__fish_vendor_confdirs paths for *.fish
  • $__fish_sysconf_dir/config.fish
  • $__fish_config_dir/config.fish

Lazy load

In fish, functions and completions are lazily loaded, means they don't get sourced until they're typed or ran. For example if we want to run custom function hello it needs to be stored in (ex. $__fish_config_dir/functions/hello.fish). There are two side-effects:

  1. If there are two identically named function files only first one will be sourced.
  2. If there additional commands in function file or even different function they will be called once

New paths can be added by appending to fish_function_path and fish_complete_path variables with no reload required, if there multiple identical functions/completions in the path first found takes precedence.

Lookup paths for functions:

  • $__fish_config_dir/functions
  • $__fish_sysconf_dir/functions
  • $__fish_vendor_functionsdirs
  • $__fish_data_dir/functions

Lookup paths for completions:

  • $__fish_config_dir/completions
  • $__fish_sysconf_dir/completions
  • $__fish_vendor_completionsdirs
  • $__fish_data_dir/completions
  • $__fish_user_data_dir/generated_completions - generated by fish_update_completions command

Reference

zsh

Shell modes

Login shell

zsh -l or zsh --login to run as login shell

if [[ -o login ]]
then
  echo 'login shell'
else
  echo 'non-login shell'
fi

Interactive shell

zsh -i or zsh --interactive to run as interactive shell

if [[ -o interactive ]]
then
  echo 'interactive shell'
else
  echo 'non-interactive shell'
fi

Startup source order

$ZDOTDIR is used as root of zsh config, if not set, $HOME is used instead.

Zsh startup order is relatively more straightforward than in Bash:

Path Comment
/etc/zsh/zshenv always sourced, --no-rcs/-f prevents next files from being sourced
$ZDOTDIR/.zshenv always sourced
/etc/zsh/zprofile login shell
$ZDOTDIR/.zprofile login shell
/etc/zsh/zshrc interactive shell
$ZDOTDIR/.zshrc interactive shell
/etc/zsh/zlogin login shell
$ZDOTDIR/.zlogin login shell
/etc/zsh/zlogout sourced on exit of login shell
$ZDOTDIR/.zlogout sourced on exit of login shell

Reference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment