Switching profiles with Fish shell
Table of Contents
How can I automatically switch profiles in my fish shell when I enter specific directories (e.g.
~/personal, …)?. This is the idea that came to my mind yesterday evening, so I set out to find a way to implement it… While I’m aware that tools like direnv would solve part of my problem, I wanted to refresh/improve my fi(sh) scripting skills and I didn’t want to be limited to manipulating environment variables.
When I enter one of my marked directories (e.g.
~/work), I want to modify my fish shell environment, for example by appending a directory to
$fish_user_paths, loading a ssh identity, and so on. Such changes are persisted for the duration of the session, unless I switch to another marked directory (e.g.
First attempt: redefine
I originally set out to define a custom
cd function that would check the value of
$PWD and switch profiles accordingly (when applicable). I got to a working solution but eventually figured out that
cd - had stopped working; this turned out to be caused by the fact that
fish itself provides a
cd function that wraps the builtin
cd command to enable that behaviour (by keeping the history of recently visited directories); after a few unsuccessful attempts at wrapping my own function around that one, I set out to find another solution.
Second attempt: listen for changes to
Digging through the
fish documentation, I found out about one of its amazing features: enabling the definition of functions that react to events or changes to variables’ values (
--on-variable, respectively; see
man function for more details). The latter looked (and proved to be) perfect for my use case, since it would allow me to react to changes to
$PWD! After several iterations, I ended up with:
- functions manipulating ssh identities (adding and removing them)
- functions manipulating
$fish_user_paths(adding and removing elements from it)
- a script, invoked when I enter a given directory, that applies changes to my environment
- a function (
__switchenv.fish) reacting to changes to
$PWDand triggering the functions that undo any previous modifications, and eventually executes the above script
The last step, which got me puzzled for 30 good minutes, was to make the
switch-env.fish function autoload on every new shell. Since I had never written event handling functions before, I expected it to work by just dropping them in
~/.config/fish/functions/ as usual, but this was not the case; digging in the documentation, I eventually reached the funcsave documentation), which states that
[…] because fish loads functions on-demand, saved functions will not function as event handlers until they are run or sourced otherwise. To activate an event handler for every new shell, add the function to your shell initialization file instead of using
Based on this, I added the following instruction to
functions -c __switchenv switchenv
This instruction creates a copy of the
__scriptenv function named
fish to load it.
Complete example #
A complete example is available in this Github repository.