Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set $PATH persistently? #527

Closed
dag opened this issue Jan 18, 2013 · 58 comments
Closed

How to set $PATH persistently? #527

dag opened this issue Jan 18, 2013 · 58 comments
Milestone

Comments

@dag
Copy link
Contributor

dag commented Jan 18, 2013

To prepend to $PATH persistently, since fish 3.2.0:

 fish_add_path /path/to/add

Top comment hijacked by @faho and @ridiculousfish to help people landing here from search engines. Original comment follows:


I try set -U PATH ~/.cabal/bin $PATH which sets it in the shell I type it in, but not any new shells I launch and it's gone if I restart the first shell as well.

Using git HEAD on Fedora 18.

@siteshwar
Copy link
Contributor

I think it's because PATH variable is handled specially in Fish. So you can't make it universal etc. I am not aware about the reasons behind it. May be @ridiculousfish or @JanKanis would like to say few words about it.

On Linux I set PATH in ~/.config/fish/config.fish this way :

set -gx PATH /opt/qt/Tools/QtCreator/bin /opt/qt/5.0.0/gcc_64/bin $PATH

@JanKanis
Copy link
Contributor

PATH is normally a global variable, created when fish starts from the environment variable in its environment. If you do set -U PATH <something> that creates a universal variable PATH, but the universal one is shadowed by the global one, so you won't see it. It could work if you do set -eg PATH (delete the global PATH), and you also need to specify the -x flag when you create the universal PATH (set -Ux PATH <something>). But if you execute e.g. a new X terminal, the fish in it again finds a PATH in its environment so it again creates a global PATH. I would recommend you set your path by doing

set PATH <mydir> $PATH

in your config.fish, so it gets executed every time fish starts. That just modifies the existing global PATH.

@dag
Copy link
Contributor Author

dag commented Jan 18, 2013

Ah. I was just wondering if there was a neatly interactive/"live" way to do it.

@JanKanis
Copy link
Contributor

config.fish lives (by default) in ~/.config/fish/config.fish, and it is
mentioned in the user docs. (But maybe it should be made more clear).
There's also vared for interactive editing of variables (however it only
works on one array element at the time).

On 18 January 2013 21:07, Dag Odenhall notifications@github.com wrote:

Ah. I was just wondering if there was a neatly interactive/"live" way to
do it. config.fish doesn't seem to be documented either so I was
wondering if there even was such a file.


Reply to this email directly or view it on GitHubhttps://github.com//issues/527#issuecomment-12438806.

@dag
Copy link
Contributor Author

dag commented Jan 18, 2013

Yea I was looking in the wrong page. I edited that comment but github sent the mail notification faster.

@ridiculousfish
Copy link
Member

When fish starts, it modifies PATH to include its bin directory. We really want to be able to tell users that they can permanently modify PATH via set -U. So we sort of want a two-level PATH - what fish adds local to the current session, and also what the user specifies, universally across all sessions.

@ridiculousfish
Copy link
Member

Since setting $PATH is very common, I think it's important to have a story for fish 2.0 that doesn't require the user to edit config.fish. However, simply making it universal is dangerous, because fish is relocatable. If fish is run from /usr/local/bin and also from ~/github/fish/, we want to allow these instances to have distinct PATHs because they have distinct $__fish_bin_dirs. On the other hand, we also want to allow the user to specify one of those bin directories directly.

Here's what I'm thinking:

  1. A variable $fish_user_paths which will be universal. We encourage the user to set it.

  2. A variable $fish_default_paths which will be global (per-process), and that will be set in share/config.fish. It will have at a minimum $__fish_bin_dir, which is fish's bin directory.

  3. $PATH will be global and exported.

  4. There will be an event handler that watches for changes to $fish_user_paths, and updates $PATH like so:

    function __fish_update_path --on-event path-var-changed
    set -gx PATH $fish_user_paths $fish_default_paths
    end

perhaps with some uniqueing to avoid duplicates.

With this design, then:

  1. Setting PATH in config.fish will continue to work, unless something changes $fish_user_paths, which fish will never do on its own.
  2. To add a path to PATH universally, set $fish_user_paths.

I'm not super-happy with this because it requires three variables - it would be nice if there were an approach that only required $PATH. Thoughts?

@JanKanis
Copy link
Contributor

JanKanis commented Feb 9, 2013

Another option that came to mind (though I'm not necessarily advocating for
it, just listing it as another option): Drop the restriction that $PATH can
only contain valid directories, then make it universal. That way a user
could just add all the directories that are possibly needed. Hmm, on second
thought, fish also needs the option of adding its own bindir to PATH,
without clobbering up user settings, so that brings my thinking to a
solution containing a user-settable path and a fish-settable path, i.o.w.
something like you propose.

Another design choice could be to rename $fish_user_paths to the universal
$PATH, and shadow it by a global $PATH, that would save a variable but it's
probably more confusing for users, so not really worth it.

On 8 February 2013 23:17, ridiculousfish notifications@github.com wrote:

Since setting $PATH is very common, I think it's important to have a
story for fish 2.0 that doesn't require the user to edit config.fish.
However, simply making it universal is dangerous, because fish is
relocatable. If fish is run from /usr/local/bin and also from
~/github/fish/, we want to allow these instances to have distinct PATHs
because they have distinct $__fish_bin_dirs. On the other hand, we also
want to allow the user to specify one of those bin directories directly.

Here's what I'm thinking:

  1. A variable $fish_user_paths which will be universal. We encourage
    the user to set it.

  2. A variable $fish_default_paths which will be global (per-process),
    and that will be set in share/config.fish. It will have at a minimum
    $__fish_bin_dir, which is fish's bin directory.

  3. $PATH will be global and exported.
    4.

    There will be an event handler that watches for changes to
    $fish_user_paths, and updates $PATH like so:

    function __fish_update_path --on-event path-var-changed
    set -gx PATH $fish_user_paths $fish_default_paths
    end

perhaps with some uniqueing to avoid duplicates.

With this design, then:

  1. Setting PATH in config.fish will continue to work, unless something
    changes $fish_user_paths, which fish will never do on its own.
  2. To add a path to PATH universally, set $fish_user_paths.

I'm not super-happy with this because it requires three variables - it
would be nice if there were an approach that only required $PATH. Thoughts?


Reply to this email directly or view it on GitHubhttps://github.com//issues/527#issuecomment-13315434..

@ridiculousfish
Copy link
Member

Having a universal PATH that just accumulates seems harmless. Another thought I had was to try to fix fish so it doesn't depend on PATH to find its own binaries. Then it wouldn't need to modify PATH at all.

@ridiculousfish
Copy link
Member

It strikes me that PATH ought never to be a universal variable, because it must be inherited from the environment. That is, if someone sets PATH in bash and then invokes fish, fish ought to respect that, and not overwrite it a universal value.

Here's what I ended up doing:

  1. fish no longer modifies PATH for its own purposes. It knows how to find its own binaries (like fish_indent) without PATH.
  2. Introduced the variable $fish_user_paths. When you modify fish_user_paths, a little function (defined in share/config.fish) runs which performs the same modifications to PATH. fish_user_paths is intended to be universal.

So this is similar to my proposal, except we don't need fish_default_paths. And I think the separation between "here's stuff from the environment" and "here's stuff to add to it" is good. I also like that it required no changes to fish proper, just to config.fish.

So now to append to PATH persistently, append to fish_user_paths, e.g. set -U fish_user_paths ~/bin . I added a note in the documentation too.

To git@github.com:fish-shell/fish-shell.git
2f43584..d3e9183 master -> master

@saulshanabrook
Copy link

Is there a reason we don't prepend to PATH instead of append? That way use set paths will take precedence, which is my desired use case, at least for Homebrew (/usr/local/bin).

@ridiculousfish
Copy link
Member

In fish 2.1, we do prepend.

@KamilaBorowska
Copy link
Contributor

@saulshanabrook: Already fixed with issue #888.

Anyway, I think that universal PATH could be possible, if the PATH would be considered to be just a hint. All directories in PATH would be added to PATH received from environment, unless they already exist, so unless you would really try, you wouldn't break fish.

@sjatkins
Copy link

sjatkins commented Nov 1, 2013

Not having PATH simply changeable by a normal user action is a major issue and contrary to the beautiful simplicity of fish relative to other shells. Please fix this. It is really a royal pain. I want to set the path and have it work from then on at least in current session if not universally. Do you know how much of a surprise it is that
set -U PATH $PATH my_path
results in an unchanged $PATH? It is ridiculous.

@zanchey
Copy link
Member

zanchey commented Nov 1, 2013

Part of the surprise is due to the fact that you can set universal variables and have global variables override them without warning; perhaps at least in interactive mode we should warn when setting a global or universal variable that has a more specifically-scoped variable with the same name set. This has bitten other variables as well, such as $TERM in #806.

@terlar
Copy link
Contributor

terlar commented Nov 1, 2013

EDIT: I just realized this is mentioned above.

If you just want to set the path for the current session, omit the scope and just do set PATH $PATH my_path. This will use whatever scope is used by this session, in this case global.

If you want to persistently add paths you do this through manipulating fish_user_paths. This is a universal variable, set fish_user_paths $fish_user_paths my_path.

@daenney
Copy link

daenney commented Nov 3, 2013

If I set something in $fish_user_paths like /usr/local/bin which is also added through an entry in /etc/paths on OS X /usr/local/bin isn't advanced on $PATH, it stays at the 'back'.

@ridiculousfish stated in fish 2.1 values from $fish_user_paths are prepended to $PATH but it looks like that only happens if the path being added is 'new', if it already exists it stays put.

This is causing slightly whacky behaviour on my system since which pip now returns /usr/local/bin/pip from my homebrew python install but which python return /usr/bin/python, the OS X python install.

The brew plugin in bpinto/oh-my-fish fixes it though but still.

@dideler
Copy link
Contributor

dideler commented Jan 14, 2014

Correction

The handler for fish_user_paths doesn't set $fish_user_paths as universal.
The correct way to persistently add a path to your $PATH (AFAIK) would be

set --universal fish_user_paths $fish_user_paths ~/path/name

Original post

After some reading and playing around with trying to figure out the "best" way to persistently add a path to your $PATH, I think @terlar's suggestion is best. That is

set fish_user_paths $fish_user_paths my_path
  • Specifying the -U/--universal flag like in set -U fish_user_paths $fish_user_paths my_path seems to be redundant.

  • Leaving out the $fish_user_paths as seen in some places, seems to overwrite your most recent path if you run this command twice with different paths:

    set -U fish_user_paths path1
    set -U fish_user_paths path2
  • Setting the $PATH on every shell instance (e.g. by putting set PATH $PATH my_path in your config.fish) is less efficient than a universal variable that you set once.

@denji
Copy link

denji commented Mar 6, 2014

# NOTE: There is probably a sexier nicer way to do this, but until I figure that out I am manually unsetting here.
# Unsets PATH
set -g -x PATH

# This allows us to use Homebrew versions of things (like git) rather than the pre-installed or XCode installed versions.
# See http://blog.grayghostvisuals.com/git/how-to-keep-git-updated/ for reference.
set -g -x PATH $PATH /usr/local/bin

# Sets necessary PATH defaults
set -g -x PATH $PATH /usr/bin /bin /usr/sbin /sbin

@yannickoo
Copy link

Thank you @dideler, following works fine for adding rvm again:

set --universal fish_user_paths $fish_user_paths ~/.rvm/bin

@timthelion
Copy link

Hm, well, I just did set --universal fish_user_paths $fish_user_paths ~/plan9/bin and ended up with a broken system :P so prepending $PATH isn't always the right thing™.

@mqudsi
Copy link
Contributor

mqudsi commented Oct 24, 2018

@NikhilVerma it's easier to just shadow fish_user_paths instead, like set -g fish_user_paths foo $fish_user_paths, which does not result in recursive modifications that balloon out of hand.

@sztadii
Copy link

sztadii commented Mar 14, 2019

SET fish_user_paths:/Users/sztadhaus/Library/Android/sdk

worked for me!

@jarcane
Copy link

jarcane commented Jun 29, 2019

Something I discovered today is that set has an append option. So if you want to avoid a bit of redundant typing you can do for instance:

set -Ua fish_user_paths ~/.local/bin

@fish-shell fish-shell deleted a comment Jul 16, 2019
@thernstig
Copy link
Contributor

Something I discovered today is that set has an append option. So if you want to avoid a bit of redundant typing you can do for instance:

set -Ua fish_user_paths ~/.local/bin

When this is done, the fish_variables file is updated with something like:
/home/myusername/\x2elocal/bin/

The problem I have is that I want to sync my whole ~/.config/fish/ directory between machines, since I work in multiple machines. It'd be a great way to keep an awesome shell experience wherever I go. But since tilde is expanded to /home/myusername/ I cannot get it to work. I tried these options in the file fish_variables but nothing works: \x7e/\x2elocal/bin/, $HOME/\x2elocal/bin/, "$HOME"/\x2elocal/bin/.

Another question is why ~/.local/bin is not set default as a PATH in the fish shell, as it is part of systemd spec: https://www.freedesktop.org/software/systemd/man/file-hierarchy.html

@faho
Copy link
Member

faho commented Aug 13, 2019

When this is done, the fish_variables file is updated with something like:
/home/myusername/\x2elocal/bin/

Unfortunately, the value will be expanded when set, and the tilde won't be expanded later, because that's not how $PATH works (the entry would be broken for every other program, with the exception of bash). That means this won't work, so you'll have to use a global variable for it and set it in config.fish.

Another question is why ~/.local/bin is not set default as a PATH in the fish shell

Fish does not set $PATH by default - it inherits it from your environment. Which is a good default because that means fish gets the same $PATH as other shells. If you want ~/.local/bin to be added by default, ask your distribution to add it, or edit e.g. /etc/login.defs.

@thernstig
Copy link
Contributor

thernstig commented Aug 13, 2019

@faho thanks for the great answer. In regards to ~/.local/bin I do understand it might not be something fish wants to default. At the same time, the default bash installed with e.g. Ubuntu 18.04.2 LTS (my current install) has that dir set by default in the users .profile. I understand this can be set in other ways, but I though that fish is all about "sane defaults" and that directory is actually a very common "sane default". At the same time I understand it might not be the philosophy of fish to include such a default in fish for the reasons you mentioned.

edit: sorry I just realized this discussion is off topic.

@ghost
Copy link

ghost commented Sep 9, 2019

A bit more off-topic stuff, but… my universal variable fish_user_paths is shadowed by a global variable with the same name containing an old value of the variable, but as one single element. I have no idea where it comes from and a recursive search for fish_user_paths in ~/.config/fish does not reveal much. What could be going on?

I am using fish 3.0.2.

> set -Ux fish_user_paths /usr/lib/ccache/bin/ $HOME/.local/bin ...
set: Universal variable 'fish_user_paths' is shadowed by the global variable of the same name.
> set -S fish_user_paths
$fish_user_paths: not set in local scope
$fish_user_paths: set in global scope, exported, with 1 elements
$fish_user_paths[1]: length=134 value=|/usr/lib/ccache/bin/ /home/.../.local/bin ...|
$fish_user_paths: set in universal scope, exported, with 7 elements
$fish_user_paths[1]: length=20 value=|/usr/lib/ccache/bin/|
$fish_user_paths[2]: length=21 value=|/home/.../.local/bin|
$fish_user_paths[3]: ...

set -eg fish_user_paths fixes it for the current shell but if I open a new one, it still has the old PATH.

@zanchey
Copy link
Member

zanchey commented Sep 16, 2019

@spider-mario it's ok to open a new issue, or to ask on Super User or a similar forum.

Your problem is that you have exported a universal variable; this is almost always wrong because it leaks into child fish processes as a global variable.

@boxed
Copy link

boxed commented Sep 30, 2019

I have to google this every time and I always end up reading through this thread. I suggest adding a command like fish-add-user-path that is idempotent when adding the same path multiple times, but otherwise is just syntactic sugar for setting fish_user_paths

@jonlorusso
Copy link

@boxed i've been trying to get myself adjusted to fish for about two months now. I must've landed on this thread at least a dozen times.

@ridiculousfish
Copy link
Member

fish_add_path sounds good to me if someone wants to tackle it.

@boxed
Copy link

boxed commented Oct 17, 2019

#!/usr/bin/env fish
if argparse -n 'fish-add-user-path' -N 1 'h/help' -- $argv
    ;
else
    exit
end

function add_to_path_if_not_there
    set exists 0

    # normalize path to end with /
    switch $argv
    case "*/"
        set new_path $argv
    case "*"
        set new_path "$argv/"
    end

    for x in (string split " " $fish_user_paths)
        if test "$x" = "$new_path"
            set exists 1
            break
        end
    end

    if test "$exists" = "0"
        set -Ux fish_user_paths $fish_user_paths $new_path
    end   
end

# validate that paths exists
for x in (string split " " $argv)
    if test -d $x
        ;
    else
        echo "Path '$x' does not exist"
        exit
    end
end


for x in (string split " " $argv)
    add_to_path_if_not_there $x 
end

This is the first fish script I wrote so I'm pretty sure it's crap! But it does do The Right Thing mostly. If you pass it several invalid paths it only complains about the first one, not all of them. And it might be a good idea if it can accept invalid paths if you pass a flag.. maybe.

But otherwise this seems to work, even if it's ugly :P

joshukraine added a commit to joshukraine/dotfiles that referenced this issue Nov 30, 2019
* Previous syntax was keeping asdf from adding itself at the beginning of the path.
* Reference: fish-shell/fish-shell#527 (comment)
joshukraine added a commit to joshukraine/dotfiles that referenced this issue Dec 3, 2019
* Previous syntax was keeping asdf from adding itself at the beginning of the path.
* Reference: fish-shell/fish-shell#527 (comment)
gbirke added a commit to gbirke/dotfiles that referenced this issue Jan 16, 2020
Changing the scope flag from "export" to "global" seems to fix the
issue. See also
fish-shell/fish-shell#527 (comment)
for reference
@pdxbmw
Copy link

pdxbmw commented Feb 14, 2020

@spider-mario I'm not sure if this addresses your issue, but I added set -eg fish_user_paths to ~/.config/fish/config.fish and the setting now persists all sessions for me.

echo "set -eg fish_user_paths" >>  ~/.config/fish/config.fish

grdl added a commit to grdl/dotfiles that referenced this issue Apr 9, 2020
Cleanup config.fish:
- Remove old commended lines
- Better way to set $PATH variable
(fish-shell/fish-shell#527 (comment))
@kuvaldini

This comment has been minimized.

@zanchey

This comment has been minimized.

@ElijahLynn
Copy link

ElijahLynn commented Apr 23, 2020

Sure, a function is nice, but here is a quick abbreviation that I like just as much.

abbr --add path_add "set --universal --append fish_user_paths"

usage:
path_add <space> <paste OR type path> <enter>

@boxed
Copy link

boxed commented Apr 27, 2020

@ElijahLynn that doesn't validate the path and it's not idempotent.

@boxed
Copy link

boxed commented Apr 27, 2020

Can't we reopen this? There is a bad UX problem here. Fish normally cares about this stuff!

@ridiculousfish
Copy link
Member

@boxed this issue has been pulled in many directions so I am going to lock it. I agree PATH often trips people up and there are probably things that can be improved about it. If you have a suggestion for improving how PATH gets set, please open a new issue.

@fish-shell fish-shell locked and limited conversation to collaborators Apr 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests