charles hendry
charles hendry

Reputation: 1740

Remove redundant paths from $PATH variable

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

Upvotes: 154

Views: 408649

Answers (19)

yota
yota

Reputation: 2230

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

Upvotes: 7

sabgenton
sabgenton

Reputation: 1893

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?

Upvotes: 2

Kele Huang
Kele Huang

Reputation: 1

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

PATH=

to reset the PATH value before assigning it.

Upvotes: -1

A. Rick
A. Rick

Reputation: 762

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.

Upvotes: 0

Ruben Castelletti
Ruben Castelletti

Reputation: 11

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

Upvotes: 0

MERM
MERM

Reputation: 752

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
}

Upvotes: 0

Scriddie
Scriddie

Reputation: 3811

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

Upvotes: 5

Hadmi Reda
Hadmi Reda

Reputation: 1

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

Upvotes: 0

guest
guest

Reputation: 1

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

Upvotes: -1

hovanessyan
hovanessyan

Reputation: 31463

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.

Upvotes: 142

kawerewagaba
kawerewagaba

Reputation: 1367

  1. Just echo $PATH
  2. copy details into a text editor
  3. remove unwanted entries
  4. PATH= # pass new list of entries

Upvotes: 7

Aaron Hall
Aaron Hall

Reputation: 395623

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

Upvotes: 24

Kevin Nathan
Kevin Nathan

Reputation: 31

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.

Upvotes: 3

GreenFox
GreenFox

Reputation: 1122

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)}}')";
    

Upvotes: 11

sufinawaz
sufinawaz

Reputation: 3721

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\/$//'`

Upvotes: 80

Alexander Shcheblikin
Alexander Shcheblikin

Reputation: 345

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.

Upvotes: 1

Levon
Levon

Reputation: 143122

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)

Upvotes: 1

Aaron Digulla
Aaron Digulla

Reputation: 328760

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.

Upvotes: 0

Leo
Leo

Reputation: 2072

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.

Upvotes: 0

Related Questions