36

I recently noticed the following in my cygwin profile, more precisely:

/usr/local/bin:/usr/bin${PATH:+:${PATH}}

What does it mean? Why is not just $PATH? Is this an 'if $PATH exists then add :$PATH'? My purpose is to swap the order and put the cygwin paths behind the windows path. In the past I would have

$PATH:/usr/local/bin:/usr/bin

but this confuses me. Maybe I should be doing

PATH="${PATH:+${PATH}:}/usr/local/bin:/usr/bin"

to append the : at the end of the $PATH?

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
tofutim
  • 463
  • 1
  • 4
  • 5
  • 2
    Your question title really messed with the formatting in the SE Hot Questions side bar http://i.imgur.com/g6pPmzf.png – Brad Mar 04 '16 at 18:01

3 Answers3

51

The :+ is a form of parameter expansion:

${parameter:+[word]} : Use Alternative Value.

If parameter is unset or null, null shall be substituted; otherwise, the expansion of word (or an empty string if word is omitted) shall be substituted.

In other words, if the variable $var is defined, echo ${var:+foo} will print foo and, if it is not, it will print the empty string.

The second : is nothing special. It is the character used as a separator in the list of directories in $PATH. So, PATH="/usr/local/bin:/usr/bin${PATH:+:${PATH}}" is a shorthand way of writing:

if [ -z "$PATH" ]; then
    PATH=/usr/local/bin:/usr/bin
else
    PATH=/usr/local/bin:/usr/bin:$PATH
fi

It's just a clever trick to avoid adding an extra : when $PATH is not set. For example:

$ PATH="/usr/bin"
$ PATH="/new/dir:$PATH" ## Add a directory
$ echo "$PATH"
/new/dir:/usr/bin

But if PATH is unset:

$ unset PATH
$ PATH="/new/dir:$PATH"
$ echo "$PATH"
/new/dir:

A : by itself adds the current directory to the $PATH. Using PATH="/new/dir${PATH:+:$PATH}" avoids this. So sure, you can use PATH="${PATH:+${PATH}:}/usr/local/bin:/usr/bin" if you want to, or you can use PATH="$PATH:/usr/local/bin:/usr/bin" if you prefer. The only difference is that the former might add an extra :, thereby adding your current directory to your $PATH.

terdon
  • 234,489
  • 66
  • 447
  • 667
  • 2
    Is the extra `:` harmful? – cat Mar 04 '16 at 16:30
  • 6
    @tac not really, it just adds the current directory to your `$PATH` (see @AndyB's [answer](http://unix.stackexchange.com/a/267548/22222)). That can be a security risk in some situations (say an attacker has uploaded a destructive script to your current directory and named it `ls` or something) but in most cases, you really don't mind. In fact, some systems add the current directory to the `PATH` by default anyway. – terdon Mar 04 '16 at 16:33
  • the path is the *one* place where I'd prefer they way `csh` handles then it as an array. – hometoast Mar 04 '16 at 18:59
  • This is one of the best explanations ever read on the various stack... thanks – Antonio Sesto Dec 03 '20 at 19:16
11

You're correct, it does mean 'if $PATH exists — and is not null — then add :$PATH'.

You need to check whether $PATH exists because you don't want to add the leading (or trailing) colon if $PATH is undefined. A zero-length (null) directory name in the path, as in :/usr/local/bin:/usr/bin, or /usr/local/bin:/usr/bin:, or /usr/local/bin::/usr/bin, means search the current directory.

Excerpted from man bash:

   PATH   ...
          A zero-length (null) directory name in the value of PATH indicates 
          the current directory.  A  null  directory name may appear as two 
          adjacent colons, or as an initial or trailing colon.
          ...

That's probably not what you want to do.

The following two lines do the same thing:

PATH=":/bin"        # search current directory, then /bin
PATH=".:/bin"
Wildcard
  • 35,316
  • 26
  • 130
  • 258
AndyB
  • 231
  • 1
  • 6
0

Also useful to compare alternative vs default parameter expansion.

Using :- inside ${} you get a default value like this:

${a:-xxx} returns "xxx" if a is not defined.

Using :+ inside ${} you get an alternative value like this:

${a:+xxx} returns "xxx" if a is defined.

So in the case of ${PATH:+${PATH}:} it basically says if PATH is defined, do not use just the PATH value, but rather return the alternative value of PATH:.