Remove redundant paths from $PATH variable
Asked Answered
N

19

152

I have defined the same path in the $PATH variable 6 times.

I wasn't logging out to check whether it worked.

How can I remove the duplicates?

The $PATH variable looks like this:

echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/flacs/Programmes/USFOS/bin:/home/flacs/Programmes/USFOS/bin:/home/flacs/Programmes/USFOS/bin:/home/flacs/Programmes/USFOS/bin:/home/flacs/Programmes/USFOS/bin:/home/flacs/Programmes/USFOS/bin

How would I reset it to just

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
Nuclear answered 25/7, 2012 at 13:29 Comment(3)
where have you defined it 6 times? in which files?Neils
possible duplicate of What is the most elegant way to remove a path from the $PATH variable in Bash?Battalion
See Duplicate entries in $PATH a problem?Revegetate
N
140

You just 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.

Note: This is for Linux. We'll make this clear for new coders. (` , ') Don't try to SET = these.

Neils answered 25/7, 2012 at 13:37 Comment(6)
yes, I set them permanently in bash.bashrc. So should the command be something like this? echo 'export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games' >> ~/.bashrcNuclear
the thing is that depending on your OS a chain of configurations are executed. You need to make sure the PATH variable is not overwritten later. The easiest way to do that (for one user) is to overwrite it in the user's personal .bashrc, which commonly is located in his home directory.Neils
your command sets the PATH to - $PATH(the current value of PATH) + the string /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/gam‌​es. If you want to have just the String, remove $PATH + the semicolon (:) from your command. It doesn't matter if you use echo or edit the file ~/.bashrc by hand.Neils
the bash.bashrc file is located in the /etc folder. It does not display the $PATH variable though, so I'm not sure where to edit itNuclear
in your first comment you echo to ~/.bashrc (quoting: >> ~/.bashrc), not to /etc/bash.bashrc. If you want to change the PATH for a specific user edit it in /home/<the name of the user>/.bashrc file. the /etc/bash.bashrc applies to all users.Neils
I don't think this is a correct answer. the question is for removing duplicates, not just setting the final PATHsUppsala
S
78

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:

PATH=`echo $PATH | sed -e 's/:\/home\/wrong\/dir\/$//'`
Splotch answered 20/9, 2013 at 20:58 Comment(8)
This was useful to me, as a directory was added in /etc/profile which I wished to exclude, but have no write access to /etc. Thanks :)Vannie
Protip: you can use different deliminator in the sed expression to avoid the \/ escaping: PATH=$(echo $PATH | sed -e 's|:/home/wrong/dir|$||')Wearing
This trick helps when I want to have new PATH right away, and don't want to log out of current terminal. However, to avoid messing up, one should experiment with the PATH generating command (i.e. echo $PATH | sed -e 's/:\/home\/wrong\/dir\/$//') before assigning it to PATH.Pomegranate
This won't work when the path to be deleted happens to be the 1st path in $PATH. Use this one: PATH=$(echo :$PATH: | sed -e 's,:/home/wrong/dir:,:,g' -e 's/^://' -e 's/:$//')Maquette
didn't work $ PATH=echo $PATH | sed -e 's/:\/scratch\/sjn\/anaconda\/bin\/python\/$//'`` for removing /scratch/sjn/anaconda/bin/pythonFincher
I used as the one recommended by iNecas but without the dollar sign: PATH=$(echo $PATH | sed -e 's|:/home/wrong/dir||')Aklog
You can also just simply state PATH=${PATH/':/home/wrong/dir'/} in your startup file... That will remove the first occurrence of that pattern. If for some reason you have it multiple times, then this will do: PATH=${PATH//':/home/wrong/dir'/}. And the quotes matter :-)Portulaca
I tried each of Stefan van den Akker, iNecas, biocyberman, and user3804598. As I did not close and reopen the terminal after each trial I didn't see any changes after each of them. So, I can't tell which of the versions finally did the job. My feeling is, it was user3804598 since this was the last I tried but I am not sure. Anyway, thanks for all contributions here!Clift
B
23

Linux: Remove redundant paths from $PATH variable

Linux From Scratch has this function in /etc/profile

# Functions to help us manage paths.  Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
        local IFS=':'
        local NEWPATH
        local DIR
        local PATHVARIABLE=${2:-PATH}
        for DIR in ${!PATHVARIABLE} ; do
                if [ "$DIR" != "$1" ] ; then
                  NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
                fi
        done
        export $PATHVARIABLE="$NEWPATH"
}

This is intended to be used with these functions for adding to the path, so that you don't do it redundantly:

pathprepend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}

pathappend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}

Simple usage is to just give pathremove the directory path to remove - but keep in mind that it has to match exactly:

$ pathremove /home/username/anaconda3/bin

This will remove each instance of that directory from your path.

If you want the directory in your path, but without the redundancies, you could just use one of the other functions, e.g. - for your specific case:

$ pathprepend /usr/local/sbin
$ pathappend /usr/local/bin
$ pathappend /usr/sbin
$ pathappend /usr/bin
$ pathappend /sbin
$ pathappend /bin
$ pathappend /usr/games

But, unless readability is the concern, at this point you're better off just doing:

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

Would the above work in all shells known to man?

I would presume the above to work in sh, dash, and bash at least. I would be surprised to learn it doesn't work in csh, fish', orksh`. I doubt it would work in Windows command shell or Powershell.

If you have Python, the following sort of command should do what is directly asked (that is, remove redundant paths):

$ PATH=$( python -c "
import os
path = os.environ['PATH'].split(':')
print(':'.join(sorted(set(path), key=path.index)))
" )

A one-liner (to sidestep multiline issues):

$ PATH=$( python -c "import os; path = os.environ['PATH'].split(':'); print(':'.join(sorted(set(path), key=path.index)))" )

The above removes later redundant paths. To remove earlier redundant paths, use a reversed list's index and reverse it again:

$ PATH=$( python -c "
import os
path = os.environ['PATH'].split(':')[::-1]
print(':'.join(sorted(set(path), key=path.index, reverse=True)))
" )
Biblio answered 7/11, 2017 at 13:57 Comment(8)
+1. does pathremove() work in all shell flavors, like, csh, ksh, bash etc? btw, my /etc/profile on RHEL doesn't have partremove(), but only has pathmunge() .Ignatia
@Ignatia I make no guarantees about compatibility with all other shells. I suggest testing it out on whatever shell you're using, and if it doesn't work for your shell, you can use Python if you have it installed - I added some Python to the answer to describe how.Biblio
If I read "3.5.3 Shell Parameter Expansion" correctly, ${!PATHVARIABLE} is a kind of variable indirection, but I'm not sure how it works here. Could you explain it please ?Revegetate
@Revegetate That's a local variable in those bash functions, declared with the local builtin (which can only be used inside a function.) If you have further questions, you should probably ask a new question on the site (after searching for it first to ensure you're not creating an exact duplicate...).Biblio
This is a really interesting answer, thanks for the writeup! However, the original question of cleaning up a messed up PATH variable seems not to be solved elegantly (althoug, I admit, then when using this functions one would not end up with a messed up PATH to begin with).Lorindalorine
The ${!PATHVARIABLE} indirection construct doesn't work in dash. That's ok, I just use $PATH instead. This is relevant even in 2021, because KDE autostart login scripts in Ubuntu are run by dash, and if you want a path setting to apply to the whole DE at the user level it has to go in an autostart login script.Inexpedient
how the oneliner command is changed for XDG_DATA_DIRS?Girosol
zsh: pathremove:5: bad substitutionOfficial
M
11

Here is a one line code that cleans up the PATH

  • It does not disturb the order of the PATH, just removes duplicates
  • Treats : and empth PATH gracefully
  • No special characters used, so does not require escape
  • Uses /bin/awk so it works even when PATH is broken

    export PATH="$(echo "$PATH" |/bin/awk 'BEGIN{RS=":";}
    {sub(sprintf("%c$",10),"");if(A[$0]){}else{A[$0]=1;
    printf(((NR==1)?"":":")$0)}}')";
    
Mulder answered 13/8, 2012 at 1:50 Comment(5)
anyone tested it? is safe?Gastronome
if you don't have awk installed it cleans your path, I advise copying your path to a txt file before with echo $PATHDiscretionary
Doesn't work for me... I have awk installed, but some duplicates are not removed.Lorindalorine
This one works! except the awk is at a different place. Can just call awk directly if you use this script at the end of your bash/zshrc file. export PATH="$(echo "$PATH" | awk 'BEGIN{RS=":";} {sub(sprintf("%c$",10),"");if(A[$0]){}else{A[$0]=1; printf(((NR==1)?"":":")$0)}}')";Uppsala
I've tested the following one-liner on a Mac. If awk isn't where it is expected, the script doesn't run. if [[ -x /usr/bin/awk ]]; then export PATH="$(echo "$PATH" |/usr/bin/awk 'BEGIN{RS=":";} {sub(sprintf("%c$",10),"");if(A[$0]){}else{A[$0]=1; printf(((NR==1)?"":":")$0)}}')"; fiDiatropism
P
6
  1. Just echo $PATH
  2. copy details into a text editor
  3. remove unwanted entries
  4. PATH= # pass new list of entries
Pinchpenny answered 5/3, 2019 at 7:45 Comment(2)
I did this and manually pressed return at every ':' splitting path variables but then don't forget to manually concatenate into one string again as newlines are picked up.Bowlds
Finally a simple answer. Unfortunately it errors with WSL since some of the paths have spaces and parenthesis: -bash: syntax error near unexpected token `('.Reisinger
F
3

If you just want to remove any duplicate paths, I use this script I wrote a while back since I was having trouble with multiple perl5/bin paths:

#!/bin/bash
#
# path-cleanup
#
# This must be run as "source path-cleanup" or ". path-cleanup"
# so the current shell gets the changes.

pathlist=`echo $PATH | sed 's/:/\n/g' | uniq`

# echo "Starting PATH: $PATH"
# echo "pathlist: $pathlist"
unset PATH
# echo "After unset, PATH: $PATH"
for dir in $pathlist
do
    if test -d $dir ; then
        if test -z $PATH; then
            PATH=$dir
        else
            PATH=$PATH:$dir
        fi
    fi
done
export PATH
# echo "After loop, PATH: $PATH"

And I put it in my ~/.profile at the end. Since I use BASH almost exclusively, I haven't tried it in other shells.

Ferret answered 7/7, 2018 at 23:32 Comment(1)
+1 for the solution. Btw, it will only remove duplication paths if they're going in list one after another. You could change |uniq to |sort|uniq to fix this, but this will change order of all directories in the path which I don't think is a desirable side-effect.Ignatia
R
3

Simple and Fool-Proof

Below is a version of this popular answer with additional explanations. It requires you to remove any duplicate or unwanted PATH components by hand. While this isn't very elegant it may often be far simpler and fool-proof.

  1. echo $PATH > path.txt writes current path to text file
  2. Edit path.txt (e.g. using nano path.txt)
  3. export PATH=$(cat path.txt) sets path to be content of the file
  4. rm path.txt removes the file

Note: This is all for your current session, in case you wanted to change PATH permanently, you would have to adapt your bash set-up files (e.g. ~/.bashrc).

Rhinarium answered 12/10, 2022 at 9:34 Comment(0)
A
3

With zsh (I love this shell), removing duplicates in the PATH is as easy as:

typeset -U path

because PATH (the colon separated list) and path (the array) are linked by default. But if you want to extend this to something else like the LD_LIBRARY_PATH, you can associate the list to an array before cleaning:

typeset -T LD_LIBRARY_PATH ld_library_path
typeset -U ld_library_path
Ammadas answered 23/11, 2023 at 12:51 Comment(0)
Y
2

In bash you simply can ${var/find/replace}

PATH=${PATH/%:\/home\/wrong\/dir\//}

Or in this case (as the replace bit is empty) just:

PATH=${PATH%:\/home\/wrong\/dir\/}

I came here first but went elsewhere as I thought there would be a parameter expansion to do this. Easier than sed!.

How to replace placeholder character or word in variable with value from another variable in Bash?

Yandell answered 10/6, 2015 at 13:59 Comment(1)
% means match if at end of string, # is for the beginning.Yandell
P
1

How did you add these duplicate paths to your PATH variable? You must have edited one of your . files. (.tcshrc, or .bashrc, etc depending on your particular system/shell). The way to fix it is to edit the file again and remove the duplicate paths.

If you didn't edit any files, and you you must have modified the PATH interactively. In that case the changes won't "stick", ie if you open another shell, or log out and log back in, the changes will be gone automatically.

Note that there are some system wide config files too, but it's unlikely you modified those, so most likely you'll be changing files in your personal home directory (if you want to make those changes permanent once you settle on a set of paths)

Phosphor answered 25/7, 2012 at 13:33 Comment(0)
S
1

For an easy copy-paste template I use this Perl snippet:

PATH=`echo $PATH | perl -pe s:/path/to/be/excluded::`

This way you don't need to escape the slashes for the substitute operator.

Sabah answered 15/2, 2017 at 22:18 Comment(0)
O
0

Assuming your shell is Bash, you can set the path with

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

but like Levon said in another answer, as soon as you terminate the shell the changes will be gone. You probably want to set up your PATH in ~/.bash_profile or ~/.bashrc.

Orlantha answered 25/7, 2012 at 13:38 Comment(0)
L
0

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").

To check what the path would be when you login next time, use telnet localhost (or telnet 127.0.0.1). It will then ask for your username and password.

This gives you a new login shell (i.e. a completely new one that doesn't inherit anything from the current environment).

You can check the value of the $PATH there and edit your rc files until it is correct. This is also useful to see whether you could login again at all after making a change to an important file.

Led answered 25/7, 2012 at 13:40 Comment(1)
Instead of having to type in the username/password, just type in /bin/bash -i. A lot less hassle.Canvass
T
0

In terminal execute code bellow i want delete java Path PATH=$(tr : '\n' <<<"$PATH" | grep -x -v "/usr/lib/java/bin" | paste -sd:) Here Solution

Troxell answered 23/5, 2022 at 19:50 Comment(0)
C
0

This function works well to de-duplicate paths and keeps the order:

### remove dups in $PATH keeping order
# https://sites.google.com/site/jdisnard/path-dupes
    
function path_de-dup() {

  declare -A SPATH
  local RET_VAL
  local A

  local OIFS=$IFS
  IFS=':'
  for A in ${PATH}
  do
    [ -z "${SPATH[${A}]}" ] || continue

    # By this point no dupe was found
    SPATH[${A}]=${#SPATH[*]}

    # Reconstruct the $PATH
    if [ -z "$RET_VAL" ]
     then RET_VAL="$A"
    else RET_VAL="${RET_VAL}:${A}"
    fi

  done
  IFS=$OIFS
  PATH=$RET_VAL
  export PATH
}
Condyle answered 13/1, 2023 at 16:26 Comment(1)
simpler if you have awk: PATH= $(echo "$PATH" | awk -F: '{for (i=1;i<=NF;i++) { if ( !x[$i]++ ) printf("%s:",$i); }}' )Condyle
C
0

I defined a function that adds the new directories to the beginning of the path, removes the components that are not a directory and removes the duplicates without changing the order of the remaining directories.

function add_path() {
  readarray -t patharray < <(echo $PATH | tr ':' '\n')
  uniqpath=()
  for dir in "${@}" "${patharray[@]}"; do
    if [ -d "${dir}" ] && [[ ! "${uniqpath[@]}" =~ "${dir}" ]]; then
      uniqpath+=("${dir}")
    fi
  done
  IFS=: eval 'PATH="${uniqpath[*]}"'
}

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

$ export ORACLE_HOME=/u01/app/oracle/product/19.0.0.0/dbhome_1
$ add_path $ORACLE_HOME/bin $ORACLE_HOME/OPatch
$ echo $PATH
/u01/app/oracle/product/19.0.0.0/dbhome_1/bin:/u01/app/oracle/product/19.0.0.0/dbhome_1/OPatch:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Chewink answered 2/2, 2023 at 12:3 Comment(0)
D
0

Building on Alex Jolig's and GreenFox's one-liner above, and the complaint that AWK wasn't where the script expected it to be (which it is not, on my Mac) I started with

if [[ -x /usr/bin/awk ]]; then
  export PATH="$(echo "$PATH" | /usr/bin/awk 'BEGIN { RS=":"; } { sub(sprintf("%c$", 10), ""); if (A[$0]) {} else { A[$0]=1; printf(((NR==1) ?"" : ":") $0) }}')"
  echo $PATH
else
  echo "AWK is not located at /usr/bin/awk" # for the truly paranoid
fi

Converting this back to a one-liner, it becomes

if [[ -x /usr/bin/awk ]]; then export PATH="$(echo "$PATH" | /usr/bin/awk 'BEGIN { RS=":"; } { sub(sprintf("%c$", 10), ""); if (A[$0]) {} else { A[$0]=1; printf(((NR==1) ?"" : ":") $0) }}')" ; fi

That gives you the protection of making sure that AWK is located where you expect it to be, and you can just toss this line in at the end of your .bashrc or .zshrc file.

Diatropism answered 28/2, 2023 at 1:30 Comment(0)
C
0

You can add a line at the beginning of the shell config file:

PATH=

to reset the PATH value before assigning it.

Centipede answered 17/7, 2023 at 16:5 Comment(0)
H
-1

PATH=echo $PATH | sed 's/:/\n/g' | sort -u | sed ':a;N;$!ba;s/\n/:/g'

Hammett answered 29/9, 2020 at 4:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.