43

I am using fish as my shell in Debian and recently (after some upgrade) whenever I try to use command completion I have:

set: No such file or directory
set: Could not add component /usr/lib/x86_64-linux-gnu/libfm to PATH.
set: No such file or directory

Running this:

echo $PATH 

Gives me this:

/usr/lib/x86_64-linux-gnu/libfm /usr/local/bin /usr/bin /bin /usr/local/games /usr/games

In my system there is no /usr/lib/x86_64-linux-gnu/libfm, so I understand why fish is complaining, but I cannot find how to remove this path from my $PATH variable.

Does anyone know how can I do this?

2
  • grep -R /usr/lib/x86_64-linux-gnu/libfm ~/.config/fish /usr/share/fish ?? Jul 2, 2014 at 16:10
  • $grep -R /usr/lib/x86_64-linux-gnu/libfm ~/.config/fish /usr/share/fish /home/superuser/.config/fish/fish_history:grep -R /usr/lib/x86_64-linux-gnu/libfm ~/.config/fish /usr/share/fish
    – tomekK
    Jul 3, 2014 at 6:16

6 Answers 6

64

The 'fish' way of setting the $PATH variable is to actually use set --universal fish_user_paths $fish_user_paths /new/path/here. Then $fish_user_paths is actually prepended to the $PATH variable when a new session starts. The $PATH documentation doesn't currently tell you how to delete it though.

In fish every variable is actually a list (array), and you can conveniently access each item directly by using an index/indice. echo $fish_user_paths will print out a space delimited version of every item in the list, make the spaces newline with the translate function echo $fish_user_paths | tr " " "\n" and then put line numbers on it with the number lines function, echo $fish_user_paths | tr " " "\n" | nl. Then delete it with set --erase --universal fish_user_paths[5]. You must use --universal or it will not work in any new sessions.

If someone has the time, please submit a PR to the repo with this example. I opened an issue here.

tldr;

  1. echo $fish_user_paths | tr " " "\n" | nl // get the number of the one you want to delete, e.g. the 5th one
  2. set --erase --universal fish_user_paths[5] // erase the 5th path universally so it persists in new sessions
6
  • 8
    Why is this so arcane? Shouldn't this be a build in feature and added to the documentation so that people doesn't have to Google and end up here? Oh well.. Jan 30, 2018 at 10:12
  • I think if we take @clozach's answer here superuser.com/a/1212305/30982, which is the addpaths() and removepath() function, and open a PR then that would be the first step to getting it added. Most of the maintainers are pretty good to work with, one seems grumpy to me, but overall I think we would have a good chance. So that is our next step. Oct 10, 2018 at 16:13
  • echo $fish_user_paths doesn't return anything, but echo $PATH returns some stuff. Jul 28, 2021 at 16:46
  • What does echo $SHELL return? It should return /usr/bin/fish or something like that. My suspicion is that it is a different shell. If it is fish, then what is fish --version? Jul 29, 2021 at 0:05
  • 2
    This set --erase --universal fish_user_paths[4] doesn't work for me in v3.3.1 for some reson. No errors, but the path is not being deleted from the list.
    – demisx
    Aug 2, 2021 at 16:16
25

As Elijah says, best practice is to modify the fish_user_paths rather than the global PATH. To avoid ever having to Google this again…

  1. Create a couple of functions that only modify fish_user_paths
  2. Make both functions autoloading

To add to user paths:

function addpaths
    contains -- $argv $fish_user_paths
       or set -U fish_user_paths $fish_user_paths $argv
    echo "Updated PATH: $PATH"
end

To remove a user path if it exists (partial credit to this):

function removepath
    if set -l index (contains -i $argv[1] $PATH)
        set --erase --universal fish_user_paths[$index]
        echo "Updated PATH: $PATH"
    else
        echo "$argv[1] not found in PATH: $PATH"
    end
end

And of course, to make them autoloading:

funcsave addpaths; funcsave removepath

Example Usage:

> addpaths /etc /usr/libexec
Modifying PATH: /usr/local/bin /usr/bin /bin /usr/sbin /sbin
Updated PATH: /etc /usr/libexec /usr/local/bin /usr/bin /bin /usr/sbin /sbin

> removepath /usr/libexec
Modifying PATH: /etc /usr/libexec /usr/local/bin /usr/bin /bin /usr/sbin /sbin
Updated PATH: /etc /usr/local/bin /usr/bin /bin /usr/sbin /sbin
5
  • Note: I've just discovered that this approach won't remove a path added through some other mechanism; Some dot-file cleanup may be in order if you find a path coming back each time you open a new terminal session.
    – clozach
    Aug 29, 2017 at 19:51
  • This is great, just added to my local! Nov 29, 2017 at 0:07
  • This is not working for me. It just prints the back to me, thats all. Jul 28, 2021 at 16:45
  • 2
    A better solution, added in a more recent version of fish, is to use the fish_add_path command. fishshell.com/docs/current/cmds/fish_add_path.html
    – m0j0
    Aug 4, 2021 at 18:41
  • The removepath function is only working partially because there is a difference between $fish_user_paths and $PATH. I think only variables added with fish_add_path go to $fish_user_paths. Therefore the "set --erase ..." line only works with vars added via fish_add_path. If you replace the "set --erase ..." line with "set -e PATH[$index]" it should work but then it won't delete PATH-vars added via fish_add_path. Doh! Jun 9, 2023 at 10:40
7

This should erase paths 6 through the last path:

set -e PATH[6..-1]

The -e flag is erase. See help set.

1
  • this will only work for the current session
    – Daniel
    Nov 4, 2019 at 12:36
3

I have made a fish function following @Elijah Lynn's answer that shows the added fish user added path entries and asks which ones you wish to remove and does so.

It's my very first fish shell script so it's probably not very good but it gets the job done. Here is the code:

function fish_remove_path --description "Shows user added PATH entries and removes the selected one"
    echo "User added PATH entries"
    set -l PATH_ENTRIES
    echo $fish_user_paths | tr " " "\n" | nl
    echo "Select the number of entry to be removed, if more than one separate the values by spaces"
    read -d " " -a PATH_ENTRIES
    for entry in $PATH_ENTRIES
        if string match -qr '^[0-9]+$' $entry
            # "$entry it is a number!"
            set -l FISH_ENTRIES (count $fish_user_paths)
            if test $entry -gt $FISH_ENTRIES
                echo "Index out of bounds, must be between 1 and $FISH_ENTRIES" 1>&2
            else
                echo "Erasing $fish_user_paths[$entry]"
                echo "Press y to continue"
                set -l confirmation
                read confirmation
                if test "$confirmation" = y
                    set --erase --universal fish_user_paths[$entry]
                else
                    echo "skipping..."
                end
            end
        else
            echo "Provided argument $entry is not a number" 1>&2
        end
    end
end

In order to use it just create a file called fish_remove_path.fish inside ~/.config/fish/functions/. Then to use it just type fish_remove_path.

Here is an example

❯ fish_add_path /home/


❯ fish_remove_path
User added PATH entries
     1  /home
     2  /opt/homebrew/bin
     3  /opt/homebrew/anaconda3/bin/
Select the number of entry to be removed, if more than one separate the values by spaces
read> 1
Erasing /home
Press y to continue
read> y

❯ echo $fish_user_paths
/opt/homebrew/bin /opt/homebrew/anaconda3/bin/

You can give it more than one entry, let's say you want to erase entries 4, 5 and 6. You just have to input them separated by space:

❯ fish_remove_path
User added PATH entries
     1  /home
     2  /opt/homebrew/bin
     3  /opt/homebrew/anaconda3/bin/
     4  /whatever1/
     5  /whatever2/
     6  /whatever3/
Select the number of entry to be removed, if more than one separate the values by spaces
read> 1 4 5 6
Erasing /home
Press y to continue
read> y
Erasing /whatever1/
Press y to continue
read> y
.....

I hope someone finds this useful, if anyone knows how to do this in a better way then please let me know.

2
  • this is awesome, thank you!
    – fent
    Mar 6, 2023 at 23:09
  • Use sed 's/ \//\n\//g' instead of tr to handle spaces in paths. May 2, 2023 at 21:39
2

Reset fish_user_paths withtout the path you don't want anymore:

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

More info: https://fishshell.com/docs/current/tutorial.html#tut_path

0

Old question, I know, but dusted off by a separate new answer recently. The question, and existing answers, sound to me a bit like:

Every time I get out of bed, I cut my foot on the broken glass on floor next to the bed. What should I do?

  1. Put a bandage on your foot every time you cut your foot after getting out of bed?
  2. Or clean up the broken glass?

I hate to be so pedantic, but it seems to me that all of the existing answers (over many years) here have essentially recommended the "bandage" option.

Instead, shouldn't we focus on fixing the root issue?!

In my system there is no /usr/lib/x86_64-linux-gnu/libfm, so I understand why fish is complaining, but I cannot find how to remove this path from my $PATH variable.

In the case of this question, why is this directory being erroneously added in the first place? Where is it being added? The best way (it seems to me) to remove a directory from the $PATH variable is to prevent it from being added in the first place (clean up the broken glass) rather than attempting to fix it after-the-fact.

Finding where it was added can take a little debugging, but it shouldn't be too difficult. There are several places to look that I can think of:

System configuration

after some upgrade

The path being referenced is part of the PCMan File Manager, but I couldn't find any issues regarding its library directory being added to the path.

However, if it was a system upgrade that changed this, then start by looking at the system path:

sudo -e /etc/environment

If that's not it, then next check to see if the directory shows in the PATH when running as root:

sudo -s
# Fish should be the $SHELL which was executed, but if not ...
fish
set --show PATH

If the bad directory is still present, then it is likely a system configuration issue. If not, then it's likely in the user configuration.

For system, of course, see if you can find a reference to the bad path somewhere in /etc:

sudo grep -r libfm /etc

There's not much other than /etc/environment that should impact Fish, but it's worth a check.

There shouldn't, as far as I know, be any Systemd units that impact this. Systemd doesn't (at the time of this answer) have the ability to set the user environment without some fairly extreme gymnastics (i.e. JSON user records, which just aren't used much yet). That said, at some point it may be worth checking a sudo grep -rs libfm /usr/lib/systemd (the -s suppresses messages on binary files matching).

User-level shell configuration

If it is user-level configuration, then check your user config, of course. I'd start with:

grep -r libfm ~/.config ~/.local

If needed, expand the search:

grep -r libfm ~

You could also substitute grep -r PATH ~ in there to look for any line changing the PATH. It's possible that the libfm directory could be part of another variable that is being used to change the PATH.

Universal variable

Speaking of, there's always the chance that it is being added via $fish_user_paths. However, there's a comment from the OP in one of the deleted answers here that indicates that was not the case here.

But for other readers, if it is the case, some of the above answers might be right! To check this, however, simply:

set --show fish_user_paths

If the bad directory is actually in there, then yes, simply remove it based on the index (best example in @ElijahLynn's answer). For instance, if $fish_user_paths[3] is the erroneous path, then:

set --erase --universal fish_user_paths[3]

In the even smaller chance that it is some other variable being set and used in a PATH modification:

set --show | grep libfm

Parent process environment

Finally, there's the chance that the parent process of your shell is modifying the environment before starting Fish. This would be rare for this type of issue, but it can certainly happen. pstree and look for any common parent process of your shell. Typically, this will be login, init (WSL systems), systemd itself, or something (not sure what it would be on macOS).

Use ps -o ppid -p $fish_pid to get the parent process of your shell, then:

sudo -s
cat /proc/<pid>/environ

Is the erroneous path there? If so, then you'll need to determine why it is being set in that process (or one of its parents) to remove it.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .