162

I'm currently playing around with the fish shell and I'm having some trouble wrapping my head around how the PATH variable is set. For what it's worth, I'm also using oh-my-fish.

If I echo my current path I get:

➜ fish echo $PATH
/usr/local/bin /usr/bin /bin /usr/sbin /sbin /usr/local/bin /opt/X11/bin /usr/texbin /Users/myname/.opam/system/bin

Looking at ~/.config/fish/config.fish I see the following line

set PATH /usr/local/bin $PATH /Users/myname/.opam/system/bin

My question is (and this phrasing will probably reflect my lack of knowledge on the subject): prior to config.fish being processed, where is the PATH variable set? ie: where do all of the paths between /usr/local/bin and /Users/myname/.opam/system/bin come from?

Josh Tilles
  • 1,281
  • 11
  • 19
Paymahn Moghadasian
  • 9,301
  • 13
  • 56
  • 94
  • the documentation describes the initialization files: http://fishshell.com/docs/current/index.html#initialization – glenn jackman Oct 05 '14 at 23:42
  • 1
    Ah. I tried finding the second file (```/etc/fish/config.fish```) and couldn't find it. I suspected it's because I installed fish with homebrew. I ran ```find / -name config.fish``` to find all the ```config.fish``` files on my system. The result was: ```/Users/name/.config/fish/config.fish```, ```/Users/name/.oh-my-fish/templates/config.fish```, ```/usr/local/Cellar/fish/2.1.0/etc/fish/config.fish```, ```/usr/local/Cellar/fish/2.1.0/share/fish/config.fish```, and ```/usr/local/etc/fish/config.fish```. I checked all of the results and none of them seemed to specify any PATH value. – Paymahn Moghadasian Oct 06 '14 at 03:56

8 Answers8

225

As stated in the official fish tutorial, you can modify the $fish_user_paths universal variable.

Run the following once from the command-line:

set -U fish_user_paths /usr/local/bin $fish_user_paths

This will prepend /usr/local/bin permanently to your path, and will affect the current session and all future instances too because the -U argument will make the variable universal.

From the fish documentation:

... (Note: you should NOT add this line to config.fish. If you do, the variable will get longer each time you run fish!)

fish_user_paths, a list of directories that are prepended to PATH. This can be a universal variable.

Community
  • 1
  • 1
Kasper
  • 12,594
  • 12
  • 41
  • 63
  • 30
    Do not add this to your fish_config file! It should be executed as a command. – Nishant Dec 23 '16 at 14:50
  • 7
    `-U` is not required since `fish_user_paths` is already universal. – kyb Mar 27 '19 at 23:36
  • 11
    Since fish 3.x PATH will not grow, fish checks if any particular path from fish_user_paths exist in PATH and if not - prepends, if yes removes it and prepends. – kyb Mar 27 '19 at 23:38
  • So this "fish_user_paths" is an actual path or command? Or do I have to give my actual path like /folder1/folder2? @Phlogi – Pranav Jun 02 '20 at 03:48
  • set -U fish_user_paths /opt/bin is the full command, while fish_user_paths is a fish shell variable. – Phlogi Jun 03 '20 at 05:02
  • @kyb: The documentation says it can be a universal variable. To check, use set --show fish_user_paths – Phlogi Jun 03 '20 at 05:07
  • 1
    So much for the easier to use shell! It doesn't make the shell game any less confusing. Hey, at least you get a prettier prompt. – ATL_DEV Feb 25 '22 at 21:09
  • @Nishant If not in the config file, where else should it be added? – Philipp Ludwig Jul 10 '22 at 10:13
  • @PhilippLudwig, you can hard code it I guess. `PATH=$PATH:...` is never good in config files in general ... It keeps appending each time! Now sure what is the best way to do this in fish though. I am using bash mostly nowadays. – Nishant Jul 10 '22 at 11:33
  • Please delete this answer or update it, it is no longer the preferred way to do this! – dessalines May 29 '23 at 20:30
168

Current answer

As of fish 3.2.0, released in March 2021, the canonical approach is:

fish_add_path /opt/mycoolthing/bin

This works both one-off interactively, and in config.fish — it won't produce duplicates in the config.fish case.


Previous answer for fish < 3.2.0:

The recommended commands for modifying PATH from fish's maintainers are:

  1. If you want to run the command once:

    set -Ua fish_user_paths /path
    
  2. If you want to add a command to a startup script, this is idempotent:

     contains /path $fish_user_paths; or set -Ua fish_user_paths /path
    
Maximilian
  • 7,512
  • 3
  • 50
  • 63
  • 2
    @Maximilian Thanks! the second part of this answer definitely helped me fix slow start up in fish sessions. I had been using something more like the first command in my config.fish file. https://github.com/fish-shell/fish-shell/issues/6136 – erran Nov 13 '20 at 10:46
12

Like all shells, fish inherits its PATH from the environment it is started in. How this is set for login shells differs between operating systems - on Linux, for example, /etc/login.defs controls the initial PATH set for all login shells. I don't know how this is set on OS X.

Next, like bash or csh, the initialisation files for the shell may alter the path. For fish on OS X, there is code in share/fish/config.fish to load paths from the standard OS X path configuration files /etc/paths and /etc/paths.d/*. There is alternative code to set a useful path on Solaris.

There is also code to pick up paths from the universal variable $fish_user_paths, which is the right way to add something to your PATH and have it reflected across all shells.

Zanchey
  • 1,941
  • 1
  • 15
  • 23
6

1. Enumerate user paths:

echo $fish_user_paths | tr " " "\n" | nl

2. Append a new bin path, permanently:

set -ga fish_user_paths my_appended_path

3. Remove 7th bin search path by index: (see #1):

set —eg fish_user_paths[7]
jorjun
  • 115
  • 1
  • 2
  • 6
5

The way that worked with me :

in your ~/.config/fish/config.fish add the following line:

set -gx PATH /path/to/dir1 /path/to/dir2 $PATH This will append those directories to your $PATH Environment variable.

Tarik Waleed
  • 97
  • 1
  • 8
3

In order to set variables for shell config file, I did as follows:

The main command to set any user specific variable is

set -Ua fish_user_paths /path/to/bin/directory/

set -Ux fish_user_paths /usr/local/bin Run touch ~/.config/fish/config.fish, unless it exists.

  1. RUST: set -Ua fish_user_paths $HOME/.cargo/bin

  2. JAVA: set -Ua fish_user_paths /path/to/java/bin

  3. Node & nvm: make sure you have installed nvm properly then

    omf install nvm

    set -gx NVM_DIR /path/to/nvm

  4. Go: set -Ua fish_user_paths /path/to/go/bin/

  5. Scala: set -Ua fish_user_paths /path/to/scala/bin/

  6. Groovy: set -Ua fish_user_paths /path/to/groovy/bin/

  7. Maven: set -Ua fish_user_paths /path/to/mvn/bin/

  8. Gradle: set -Ua fish_user_paths /path/to/groovy/bin/

Finally, refresh your terminal.

PS in some operating system (eg OpenSuse), drop a.

The result after configuration

Esmaeil MIRZAEE
  • 1,110
  • 11
  • 14
0

Above solutions do not work well with python virtual environment. The path to virtual environment will not be the first one in the list.

In order to set path once and do not override it every time you start python virtual environment

You need to change the path conditionally in ~/.config/fish/config.fish, like this:

contains /home/$USER/.pyenv/bin $PATH; or set -x PATH "/home/$USER/.pyenv/bin" $PATH
contains /home/$USER/.local/bin $PATH; or set -x PATH "/home/$USER/.local/bin" $PAT
contains /home/$USER/.asdf/bin $PATH; or source ~/.asdf/asdf.fish

This will ensure that you have new paths in $PATH but wnen you spawn new shell for virtual environment you'll will not add these paths again.

Unfortunately paths set with set -Ua fish_user_paths would endup in the beginning of the $PATH, pushing your virtual environment path away from the first place.

Janusz Skonieczny
  • 17,642
  • 11
  • 55
  • 63
-1

You may use a script like the one below to ease adding or removing paths:

#!/usr/bin/env fish

#remove PHP72
for argv in /usr/local/opt/php@7.2/bin /usr/local/opt/php@7.2/sbin;
    while contains $argv $fish_user_paths
        set -l index (contains -i $argv $fish_user_paths)
        set –erase –universal fish_user_paths[$index]
    end
end

#add PHP74
for argv in /usr/local/opt/php@7.4/bin /usr/local/opt/php@7.4/sbin;
    if contains $argv $fish_user_paths
        echo "Path already contains $argv"
    else
fish_add_path $argv
    end
end

echo $fish_user_paths | tr " " "\n" | nl

brew services stop php@7.2
brew services start php@7.4

Copied from https://www.tai.ro/2021/10/22/how-to-switch-path-in-fish-shell-with-example-script/

Jorj
  • 2,495
  • 1
  • 19
  • 12