Technical notes › Handle Fish universal variables and abbrs across machines
A feature fish has that other shells do not is universal variables: variables that are automatically applied to other currently-running fish sessions. Fish uses them internally for its own configuration.
I think this is a really neat feature. When I used zsh, I’d occasionally edit one of my configuration files, only to find several hours or even days later that the update hadn’t been applied to one of my long-running existing shell sessions — and then I’d have to close whatever was going on in that terminal and re-open it to stay consistent.
The downside of this is feature is that it makes it slightly more awkward to keep the entirety of your fish configuration in a Git repo, or to synchronise it between machines.
Universal variables are stored in a separate file,
fish_variables, unlike the other files in fish’s configuration folder, is not intended to be human-edited.
It’s natural to define abbreviations and set universal variables in the
fish_config file, in the same place as where aliases are defined and global variables are set.
But there are some pitfalls that are easy to encounter when you try doing it this way:
- If you define an abbreviation in
fish_config, and later remove the line that defines it, the abbr will live on in
fish_variablesuntil you remove it manually.
- If you append (
-a) or prepend (
-p) to an array universal variable, it will be added again every time you start a new shell, making it get longer and longer, invisibly slowing your shell down each time until it finally gets slow enough for you to realise what’s happening.
On this page, I evaluate some ways to deal with this.
Fish version 3.0 allows you to define global abbreviations, rather than universal ones, with the
Global abbreviations will only apply to the shell session they’re defined in, and will not be made available in other previously-running fish sessions.
This means that you can place these
abbr definitions in your fish configuration file, and they will apply to only the session that they’re loading.
abbr -ga cb 'cargo build' abbr -ga cbr 'cargo build --release' abbr -ga ct 'cargo test' abbr -ga ck 'cargo check' abbr -ga cr 'cargo run --'
The downside of this is that it’s quite slow. Defining 100 abbreviations in my configuration file was enough to bring my shell startup time from 9ms to 60ms, which is enough to be noticeable.
A separate setup stage
The solution I settled on was to split my fish configuration in two.
I have the regular
fish_config file along with my function definitions and variable overrides, but I also have a
setup/ folder where everything that defines universal variables goes.
The files in this folder are not touched during fish startup, so I get to keep my nice fast startup time, while maintaining the fact that everything about my setup is stored inside the same Git repository.
To activate the changes in the
setup/ directory, I run this function that clears out all loaded universal variables and loads them anew:
function fresh-fish -d "Resets fish’s universal variables" # delete all existing universal vars # <https://github.com/fish-shell/fish-shell/issues/4542#issuecomment-430848765> set -l existing_vars \ (set --show \ | string replace -rf '^\$([^:+).*: set in universal.*' '$1' \ | grep -E '^_?fish_') for v in $existing_vars set -Ue $v end # load new universal vars source $__fish_config_dir/setup/cargo-abbrs.fish source $__fish_config_dir/setup/common-abbrs.fish source $__fish_config_dir/setup/git-abbrs.fish source $__fish_config_dir/setup/theme.fish echo "[All loaded.]" end
Then, I define my aliases throughout the
abbrs files, without needing to use
abbr -a cb 'cargo build' abbr -a cbr 'cargo build --release' abbr -a ct 'cargo test' abbr -a ck 'cargo check' abbr -a cr 'cargo run --'
I also define my fish theme in one of these files, because it too uses universal variables:
set -U fish_color_autosuggestion brblack set -U fish_color_command \x2d\x2dbold set -U fish_color_comment red set -U fish_color_end brmagenta set -U fish_color_error brred set -U fish_color_escape bryellow\x1e\x2d\x2dbold set -U fish_color_operator bryellow set -U fish_color_param cyan set -U fish_color_quote yellow set -U fish_color_redirection brblue
Now, whenever I change any of the files within the
setup/ directory, I simply run the
fresh-fish function defined above, and it re-configures my theme and abbreviations — and even applies the new versions to running sessions without me having to close and re-open them.
The obvious downside to this is that you need to remember to do it, but I’ve been doing this for several months now and it hasn’t been an issue.∎