42

I source bashrc's of few of my friends. So I end up having duplicate entries in my $PATH variable. I am not sure if that is the problem for commands taking long to start. How does $PATH internally work in bash? Does having more PATHS slow my start up time?

41

Having more entries in $PATH doesn't directly slow your startup, but it does slow each time you first run a particular command in a shell session (not every time you run the command, because bash maintains a cache). The slowdown is rarely perceptible unless you have a particularly slow filesystem (e.g. NFS, Samba or other network filesystem, or on Cygwin).

Duplicate entries are also a little annoying when you review your $PATH visually, you have to wade through more cruft.

It's easy enough to avoid adding duplicate entries.

case ":$PATH:" in
  *":$new_entry:"*) :;; # already there
  *) PATH="$new_entry:$PATH";; # or PATH="$PATH:$new_entry"
esac

Side note: sourcing someone else's shell script means executing code that he's written. In other words, you're giving your friends access to your account whenever they want.

Side note: .bashrc is not the right place to set $PATH or any other environment variable. Environment variables should be set in ~/.profile. See Which setup files should be used for setting up environment variables with bash?, Difference between .bashrc and .bash_profile.

  • 8
    +1: can't emphasize that "giving your friends access to your account" enough emphasis. Even if there is no attempt at doing you harm, their script could be just what they need and still eat your lunch when you source it. – msw Jun 13 '11 at 11:41
  • One possible issue with this solution is if $new_entry is already the first entry in PATH, the ":$new_entry:" won't match. I fixed this in my profile by excluding the initial ':' colon. – Jeff Bauer May 19 at 14:12
  • @JeffBauer I don't see the problem. I use case :$PATH: and not case $PATH so that it matches even if the entry is first or last. – Gilles May 19 at 20:05
31

I've seen people clean up duplicates from their PATH variable using awk and something like this:

PATH=$(printf "%s" "$PATH" | awk -v RS=':' '!a[$1]++ { if (NR > 1) printf RS; printf $1 }')

You could try adding that to your own bashrc and make sure you source the other files somewhere before running that.

An alternative would be to use the pathmerge utility.

As for your speed problem, this will not affect the startup time of the shell in any significant way but it may save some time doing tab completion for commands, especially when the command is not found in the path and it does repeated searches through the same folders looking for it.

A note on security: You should really heed Gilles' warnings about security here. By sourcing a file owned by another user you are giving a free pass to those users to execute their own code as your user every time you start a shell. If you don't trust those users with your password, you shouldn't be sourcing their shell files.

  • 6
    I like the awk one-liner, but it prints a trailing ORS ':'. So I modified it to read PATH=$(echo "$PATH" | awk -v RS=':' -v ORS=":" '!a[$1]++{if (NR > 1) printf ORS; printf $a[$1]}') – gkb0986 Sep 18 '13 at 6:01
  • The trailing : is not only a cosmetic issue. It is the same as adding . to your path, which is potentially dangerous. – wisbucky Apr 24 '17 at 23:31
  • I've edited the answer to include the fix from gkb0986. – Tim Lesher Jan 19 '18 at 13:24
  • @TimLesher The reason I'd never edited than into by answer is that it doesn't work for me .... and the original without it does work (including not leaving a trailing separator. I don't know what the difference is. – Caleb Jan 19 '18 at 14:07
  • @Tim printf $a[$1] can’t be right, that prints the field in the position matching the number of times the non-dupe has been seen, i.e. the first field. – Stephen Kitt Jan 19 '18 at 16:10
11

Based on @Gilles answer you may wrap it in a function to minimize typing:

function addToPATH {
  case ":$PATH:" in
    *":$1:"*) :;; # already there
    *) PATH="$1:$PATH";; # or PATH="$PATH:$1"
  esac
}

addToPATH /Applications/AIRSDK_Compiler/bin
addToPATH ~/.local/lib/npm/bin
  • 1
    Most practically-usable (high-level, perhaps) answer. – ijoseph Aug 29 '18 at 17:42
3

Only the first match in $PATH is executed, so any subsequent entries are not processed after that. That's why you should sometimes revise the order of the entries in your $PATH to make your environment behave as expected.

To answer your question: this shouldn't be the cause of slow startup.

  • 1
    But it takes longer when I type a command that does not exist. It will search the same folder twice for the command. – balki Jun 13 '11 at 8:50
  • @balki You mean completing a command with TAB? In that case you should check whether you complete definition doesn't look like complete -c which -a. You should delete the -a parameter. You can check that by issuing the command: complete | grep which. – Rajish Jun 13 '11 at 9:34
  • It could still be an issue if it searches the same directory that it's not in multiple times before finding it. – Random832 Jun 13 '11 at 13:44
-1

To prevent duplicate entries in my PATH, I had to put the following in BOTH ~/.bash_profile and ~/.bashrc:

PATH=$(echo $(sed 's/:/\n/g' <<< $PATH | sort | uniq) | sed -e 's/\s/':'/g')

The main drawback is that it sorts PATH entries, but I think I can live with that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.