Search code examples
sedpathfish

Fish: remove path from $PATH fails if $PATH edited with sed


I'm using fish 2.2.0. (Yes, I know it's old, I cannot do anything about it.)

I want to unset a path (/usr/local/ferret-6.82/bin) from the $PATH. To do this I would assume using sed would work:

> echo $PATH
/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games /usr/local/ferret-6.82/bin /opt/intel/bin

> echo $PATH | sed -e 's:/usr/local/ferret-6.82/bin ::g'
/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games /opt/intel/bin

Great! However...

> set -gx PATH (echo $PATH | sed -e 's:/usr/local/ferret-6.82/bin ::g')
set: Warning: path component /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games /opt/intel/bin may not be valid in PATH.
set: No such file or directory
> echo $PATH
/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games /usr/local/ferret-6.82/bin /opt/intel/bin

Of course all those directories exist. Setting the path manually works:

> set -gx PATH /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games /opt/intel/bin
> echo $PATH
/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games /opt/intel/bin

What is wrong here? Why does my path setting with sed fail?


Solution

  • $PATH, $CDPATH and $MANPATH are special in fish. (Since 2.2.0, it is just those specific variables. Before that, this would apply to any variable)

    They are lists with one element per component that are converted to and from the usual colon-delimited form when exporting to and importing from external programs.

    What you want to do here is to either use fish-script:

    if set -l ind (contains -i -- /usr/local/ferret-6.82/bin $PATH)
        set -e PATH[$ind]
    end
    

    The contains -i will return 0 and output the index that the first argument is at, set -e will remove that element from the list. "--" is the option-separator, after which no argument will be interpreted as an option. Note the missing quotes for $PATH, which will result in each component of $PATH being passed as a separate argument.

    Or, since fish splits command substitutions on newlines, you can operate on $PATH per-line, with

    printf '%s\n' $PATH | sed '\_/usr/local/ferret-6.82/bin_d'
    

    (Note: For the "d" command, it is necessary to escape the delimiter - "_" in this case - on first use if it's not "/")