Removing a directory from PATH

263,164

Solution 1

There are no standard tools to "edit" the value of $PATH (i.e. "add folder only when it doesn't already exists" or "remove this folder"). You can execute:

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

that would be for the current session, if you want to change permanently add it to any .bashrc, bash.bashrc, /etc/profile - whatever fits your system and user needs. However if you're using BASH, you can also do the following if, let's say, you want to remove the directory /home/wrong/dir/ from your PATH variable, assuming it's at the end:

PATH=$(echo "$PATH" | sed -e 's/:\/home\/wrong\/dir$//')

So in your case you may use

PATH=$(echo "$PATH" | sed -e 's/:\/d\/Programme\/cygwin\/bin$//')

Solution 2

In bash:

directory_to_remove=/d/Programme/cygwin/bin
PATH=:$PATH:
PATH=${PATH//:$directory_to_remove:/:}
PATH=${PATH#:}; PATH=${PATH%:}

If you don't use an intermediate variable, you need to protect the / characters in the directory to remove so that they aren't treated as the end of the search text.

PATH=:$PATH:
PATH=${PATH//:\/d\/Programme\/cygwin\/bin:/:}
PATH=${PATH#:}; PATH=${PATH%:}

The first and third line are there to arrange for every component of the search path to be surrounded by :, to avoid special-casing the first and last component. The second line removes the specified component.

Solution 3

Much simpler one liner.

export PATH=`echo $PATH | tr ":" "\n" | grep -v "anaconda" | tr "\n" ":"`

Solution 4

After considering other options presented here, and not fully understanding how some of them worked I developed my own path_remove function, which I added to my .bashrc:

function path_remove {
  # Delete path by parts so we can never accidentally remove sub paths
  if [ "$PATH" == "$1" ] ; then PATH="" ; fi
  PATH=${PATH//":$1:"/":"} # delete any instances in the middle
  PATH=${PATH/#"$1:"/} # delete any instance at the beginning
  PATH=${PATH/%":$1"/} # delete any instance in the at the end
}

and tested with

function path_remove_test {(
  PATH=$1
  path_remove $2
  if [ "$PATH" != "$3" ] ; then echo "$1" - "$2" = "$PATH" != "$3" ; fi
)}

path_remove_test startpath:midpath:endpath startpath midpath:endpath
path_remove_test startpath:midpath:endpath midpath startpath:endpath
path_remove_test startpath:midpath:endpath endpath startpath:midpath
path_remove_test somepath:mypath/mysubpath  mypath somepath:mypath/mysubpath 
path_remove_test path path ""

This ended up pretty close to Gilles' solution but wrapped up as a bash function which could be easily used on the command line.

It has the advantages that as a bash function it works like a program without needing to be a program on the path, and it doesn't require any external programs to run, just bash string manipulation.

It appears pretty robust, in particular it doesn't turn somepath:mypath/mysubpath into somepath/mysubpath:if you run path_remove mypath, which was a problem I had with my previous path_remove function.

An excellent explanation of how bash string manipulation works can be found at the Advanced Bash-Scripting Guide.

Solution 5

So, combining the answers from @gilles and @bruno-a (and a couple of other sed tricks) I came up with this one-liner, which will remove (every) REMOVE_PART from PATH, regardless of whether it occurs at the beginning, middle or end of PATH

PATH=$(REMOVE_PART="/d/Programme/cygwin/bin" sh -c 'echo ":$PATH:" | sed "s@:$REMOVE_PART:@:@g;s@^:\(.*\):\$@\1@"')

It's a bit unwieldy, but it's nice to be able to do it in one hit. The ; is used to join together the two separate sed commands:

  • s@:$REMOVE_PART:@:@g (which replaces :$REMOVE_PART: with a single :)
  • s@^:\(.*\):\$@\1@ (which strips off the leading and trailing colons we added with the echo command)

And along similar lines, I've just managed to come up with this one-liner for adding a ADD_PART to the PATH, only if the PATH doesn't already contain it

PATH=$(ADD_PART="/d/Programme/cygwin/bin" sh -c 'if echo ":$PATH:" | grep -q ":$ADD_PART:"; then echo "$PATH"; else echo "$ADD_PART:$PATH"; fi')

Change the last part to echo "$PATH:$ADD_PART" if you want to add ADD_PART to the end of PATH instead of to the start.

...

...or to make this even easier, create a script called remove_path_part with the contents

echo ":$PATH:" | sed "s@:$1:@:@g;s@^:\(.*\):\$@\1@"

and a script called prepend_path_part with the contents

if echo ":$PATH:" | grep -q ":$1:"; then echo "$PATH"; else echo "$1:$PATH"; fi

and a script called append_path_part with the contents

if echo ":$PATH:" | grep -q ":$1:"; then echo "$PATH"; else echo "$PATH:$1"; fi

make them all executable, and then call them like:

  • PATH=$(remove_path_part /d/Programme/cygwin/bin)
  • PATH=$(prepend_path_part /d/Programme/cygwin/bin)
  • PATH=$(append_path_part /d/Programme/cygwin/bin)

Neat, even if I say so myself :-)

Share:
263,164

Related videos on Youtube

Devolus
Author by

Devolus

Assembly, C/C++, SQL and Java developer.

Updated on September 18, 2022

Comments

  • Devolus
    Devolus over 1 year

    I'm trying to compile wxWidgets using MingW, and I have cygwin in my path, which seems to conflict. So I would like to remove /d/Programme/cygwin/bin from the PATH variable and I wonder if there is some elegant way to do this.

    The naive approach would be to echo it to a file, remove it manually and source it, but I bet there is better approach to this.

  • Graeme
    Graeme over 10 years
    If the path in question is at the beginning of the PATH variable, you need to match the colon at the end. This is an annoying caveat which complicates easy generic manipulations of PATH variables.
  • Matthias Kuhn
    Matthias Kuhn about 8 years
    When dealing with so many slashes I prefer to change the regex delimiter / with something like |: PATH=$(echo "$PATH" | sed -e 's|:/d/Programme/cygwin/bin$||') to prevent all the escaping.
  • flyagaricus
    flyagaricus almost 8 years
    Thanks @Gilles, your answer prompted me to come up with my own solution, which only requires three manipulations of PATH rather then four. *8')
  • Stephen Kitt
    Stephen Kitt over 4 years
    There’s already a simpler variant of this approach (but thanks for explaining yours!).
  • anax32
    anax32 over 4 years
    that version suffers from trailing delimiters
  • Stephen Kitt
    Stephen Kitt over 4 years
    Ah, right, indeed, thanks!
  • Dmitry Avtonomov
    Dmitry Avtonomov almost 4 years
    That's the only solution that i can internalize and remember without having to search for this question on SO again
  • amphetamachine
    amphetamachine over 3 years
    This unfortunately doesn't eliminate successive directory entries from the path, i.e. baz:foo:foo:bar removing foo becomes baz:foo:bar. This is because the colon on both sides of the pattern matches baz[:foo:]foo:bar and because the last match ended with the colon, it doesn't pick up with the next :foo:.
  • knia
    knia over 3 years
    This adds a : at the end of PATH every time the command is used, because grep appends an EOL (\n) to its output. Solve this by piping the output of grep through perl -pe 'chomp if eof'.
  • user1461607
    user1461607 over 3 years
    Let's use the whole swiss army knife to solve a simple problem!
  • Cole
    Cole about 3 years
    This answer explains how to make the pattern more flexible so it works regardless of whether it's at the end of the path or not: superuser.com/a/1117805 This would yield PATH=$(echo "$PATH" | sed -e 's/:\/d\/Programme\/cygwin\/bin\(:\|$\)//')
  • Albercoc
    Albercoc about 3 years
    This method fails when the path to be removed is the only one in $PATH. Add if [ $PATH = $1 ]; then PATH=""; fi;
  • John Jiang
    John Jiang about 3 years
    add another grep -v '^$' to remove empty ones.
  • MUY Belgium
    MUY Belgium about 3 years
    On which system have you your problem? $PATH is in upper case.
  • Kusalananda
    Kusalananda about 3 years
  • Kusalananda
    Kusalananda about 3 years
    This could be optimized into path=$(basename "$path") or path=${path##*/}. Note that the question asks about removing a component of the :-delimited list in $PATH. Your answer does not address this.
  • BLuFeNiX
    BLuFeNiX over 2 years
    To get rid of the trailing colon, you can also pipe the output to xargs, like so: echo $PATH | tr ":" "\n" | grep -v '/usr/local/bin' | xargs | tr ' ' ':'
  • ArrowCase
    ArrowCase over 2 years
    Thats interesting, it will be helpful if you provide an explanation. ¿What do you mean does not work for repetition?
  • Admin
    Admin about 2 years
    Thanks @Albercoc, fixed.
  • Admin
    Admin almost 2 years
    Good answer... for those like me that have fat fingers, I would recommend creating a "PRACTICEPATH" to avoid potential grief. Other than that, I think the answer might be improved by adding a "verify" step at the end to make sure "all is well" before you have to find out "the hard way".
  • Admin
    Admin almost 2 years
    What is the "anaconda" bit for?
  • Admin
    Admin almost 2 years
    @knia+ or give perl the whole job: PATH=$( echo $PATH | perl -pe '$_=join ":", grep{ !/deletewhat/ } split ":"' ). Could also use the fact command substitution deletes trailing newline(s): temp=$( echo $PATH | tr : '\n' | grep -v deletewhat ); PATH=${temp//$'\n'/:} . And in bash instead of echo $PATH | can use herestring <<<$PATH.