151

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
codeforester
  • 39,467
  • 16
  • 112
  • 140
charles hendry
  • 1,710
  • 4
  • 13
  • 18

18 Answers18

138

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.

Community
  • 1
  • 1
hovanessyan
  • 30,580
  • 6
  • 55
  • 83
  • 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' >> ~/.bashrc` – charles hendry Jul 25 '12 at 13:44
  • 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. – hovanessyan Jul 25 '12 at 13:46
  • 1
    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. – hovanessyan Jul 25 '12 at 13:47
  • 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 it – charles hendry Jul 25 '12 at 13:56
  • 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//.bashrc file. the /etc/bash.bashrc applies to all users. – hovanessyan Jul 25 '12 at 13:59
  • I don't think this is a correct answer. the question is for removing duplicates, not just setting the final PATHs – Honghao Z Aug 24 '21 at 01:34
77

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\/$//'`
Stefan van den Akker
  • 6,661
  • 7
  • 48
  • 63
sufinawaz
  • 3,623
  • 1
  • 25
  • 24
  • 1
    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 :) – Samizdis Apr 23 '14 at 15:42
  • 17
    Protip: you can use different deliminator in the sed expression to avoid the \/ escaping: `PATH=$(echo $PATH | sed -e 's|:/home/wrong/dir|$||')` – iNecas Dec 11 '14 at 21:30
  • 4
    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. – biocyberman May 01 '15 at 08:38
  • 2
    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/:$//')` – Robin Hsu Apr 13 '17 at 07:54
  • didn't work `$ PATH=`echo $PATH | sed -e 's/:\/scratch\/sjn\/anaconda\/bin\/python\/$//'`` for removing /scratch/sjn/anaconda/bin/python – Mona Jalal Mar 09 '18 at 02:10
  • I used as the one recommended by iNecas but without the dollar sign: `PATH=$(echo $PATH | sed -e 's|:/home/wrong/dir||')` – user3804598 Oct 01 '19 at 10:49
  • 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 :-) – Flandraco Apr 10 '20 at 18:54
  • 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! – soirbleu May 07 '20 at 19:31
22

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)))
" )
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
  • +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() . – Tagar Oct 21 '18 at 15:50
  • 1
    @Tagar 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. – Russia Must Remove Putin Oct 21 '18 at 18:57
  • 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 ? – loxaxs Nov 01 '18 at 13:11
  • @loxaxs 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...). – Russia Must Remove Putin Nov 01 '18 at 13:21
  • 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). – Christian Herenz Dec 10 '19 at 18:23
  • 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. – JohnLittle Aug 24 '21 at 09:32
  • how the oneliner command is changed for XDG_DATA_DIRS? – Estatistics Jul 30 '22 at 07:41
  • zsh: `pathremove:5: bad substitution` – towry Oct 19 '22 at 03:07
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)}}')";
    
Ghasem
  • 14,455
  • 21
  • 138
  • 171
GreenFox
  • 1,112
  • 10
  • 5
  • 1
    anyone tested it? is safe? – Joe RR Apr 03 '18 at 17:32
  • 2
    if you don't have awk installed it cleans your path, I advise copying your path to a txt file before with echo $PATH – Gabriel Pita Apr 18 '18 at 15:26
  • Doesn't work for me... I have awk installed, but some duplicates are not removed. – Christian Herenz Dec 10 '19 at 18:49
  • 1
    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)}}')";``` – Honghao Z Aug 24 '21 at 01:39
  • 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)}}')"; fi – A. Rick Feb 28 '23 at 01:37
6
  1. Just echo $PATH
  2. copy details into a text editor
  3. remove unwanted entries
  4. PATH= # pass new list of entries
kawerewagaba
  • 1,107
  • 2
  • 15
  • 21
  • 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. – mLstudent33 Sep 30 '21 at 17:30
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.

  • 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. – Tagar Oct 21 '18 at 15:53
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).

Scriddie
  • 2,584
  • 1
  • 10
  • 17
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 else ware 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?

Community
  • 1
  • 1
sabgenton
  • 1,823
  • 1
  • 12
  • 20
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)

Levon
  • 138,105
  • 33
  • 200
  • 191
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.

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.

Leo
  • 2,052
  • 5
  • 17
  • 24
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.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Instead of having to type in the username/password, just type in `/bin/bash -i`. A lot less hassle. – Ed Heal Jul 25 '12 at 13:47
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

Hadmi Reda
  • 1
  • 1
  • 1
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
}
MERM
  • 629
  • 7
  • 21
  • simpler if you have awk: PATH= $(echo "$PATH" | awk -F: '{for (i=1;i<=NF;i++) { if ( !x[$i]++ ) printf("%s:",$i); }}' ) – MERM Jan 16 '23 at 15:48
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
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.

A. Rick
  • 649
  • 5
  • 11
0

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

PATH=

to reset the PATH value before assigning it.

-1

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

guest
  • 1