77

I want to show my PATH environment variable in a more human-readable way.

$ echo $PATH
/Users/arturo/.rvm/gems/ruby-1.9.3-p392/bin:/Users/arturo/.rvm/gems/ruby-1.9.3-p392@global/bin:/Users/arturo/.rvm/rubies/ruby-1.9.3-p392/bin:/Users/arturo/.rvm/bin:/usr/local/git/bin:/Users/arturo/.gvm/groovy/current/bin:/Users/arturo/.gvm/grails/current/bin:/Users/arturo/.gvm/griffon/current/bin:/Users/arturo/.gvm/gradle/current/bin:/Users/arturo/.gvm/lazybones/current/bin:/Users/arturo/.gvm/vertx/current/bin:/Users/arturo/.gvm/bin:/Users/arturo/.gvm/ext:/usr/local/git/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/git/bin

I'm thinking in something like this:

$ echo $PATH | some cut and awk magic
/Users/arturo/.rvm/gems/ruby-1.9.3-p392/bin
/Users/arturo/.rvm/gems/ruby-1.9.3-p392@global/bin
/Users/arturo/.rvm/rubies/ruby-1.9.3-p392/bin
/Users/arturo/.rvm/bin
...

5 Answers5

105

You can use tr.

$ tr ':' '\n' <<< "$PATH"
/Users/arturo/.rvm/gems/ruby-1.9.3-p392/bin
/Users/arturo/.rvm/gems/ruby-1.9.3-p392@global/bin
/Users/arturo/.rvm/rubies/ruby-1.9.3-p392/bin
...

You can also do this in some shells (tested in bash and zsh):

echo -e ${PATH//:/\\n}

In zsh, you can use the $path variable to see your path with spaces instead of colons.

$ echo $path
/Users/arturo/.rvm/gems/ruby-1.9.3-p392/bin /Users/arturo/.rvm/gems/ruby-1.9.3-p392@global/bin /Users/arturo/.rvm/rubies/ruby-1.9.3-p392/bin /Users/arturo/.rvm/bin

Which can be combined with printf or print.

$ printf "%s\n" $path
/Users/arturo/.rvm/gems/ruby-1.9.3-p392/bin
/Users/arturo/.rvm/gems/ruby-1.9.3-p392@global/bin
/Users/arturo/.rvm/rubies/ruby-1.9.3-p392/bin
...
$ print -l $path
/Users/arturo/.rvm/gems/ruby-1.9.3-p392/bin
/Users/arturo/.rvm/gems/ruby-1.9.3-p392@global/bin
/Users/arturo/.rvm/rubies/ruby-1.9.3-p392/bin
...

The <<< operators are called herestrings. Herestrings pass the word to their right to the standard input of the command on their left.

$ cat <<< 'Hello there'
Hello there

If your shell doesn't support them, use echo and a pipe.

$ echo 'Hello there' | cat
Hello there
  • 2
    Interesting the use of the here-string, I'd do the following: echo $PATH | tr ':' '\n' but is more clear your solution. – Arturo Herrero Jun 20 '13 at 15:37
  • 1
    I added a bit to my answer to explain the use of herestrings to any passersby. –  Jun 20 '13 at 15:45
  • In zsh, print -l $path saves a few keystrokes. It fails if $path contains backslashes, but that's highly unusual. – Gilles 'SO- stop being evil' Jun 20 '13 at 18:10
  • Note that some of those will fail to display the empty entries in $PATH. – Stéphane Chazelas Jun 20 '13 at 19:37
  • @StephaneChazelas: I never thought about that. The first two display empty entries. The zsh-only ones do not. –  Jun 20 '13 at 19:48
  • @Gilles: For the sake of completeness, I have added that to my answer. –  Jun 20 '13 at 19:51
  • In bash, echo ${PATH//:/\\n} gives me literal \n sequences, not newlines. I don't use zsh, but I wonder what printf "%s\n" $path will do if some of the directory names contain spaces. (I just tried it, and it handles them correctly.) – Keith Thompson Jun 20 '13 at 20:20
  • Oops. For bash compatibility, that should be echo -e ${PATH//:/\\n}. I have edited my answer to fix that typo. –  Jun 20 '13 at 20:28
  • This would break if any directories in $PATH contain colons in their name; a somewhat solution would be echo $PATH | tr ':/' '\n/' -- but that would still break if a directory like /foo:/bar was in the $PATH... – evilsoup Jun 20 '13 at 23:20
  • Bash and zsh can't handle path directories with colons in their names. Take a look at the extract_colon_unit function in general.c for bash and the colonsplit function in utils.c for zsh. –  Jun 21 '13 at 00:48
  • tr ':/' '\n/' would translate : to \n and / to /, meaning it's the same as tr ':' '\n'. You could use sed instead: sed 's|:/|\n/|'. –  Jun 21 '13 at 00:58
  • @evilsoup "directory names that might be used in PATH should not include a character." –POSIX – Robin A. Meade Jul 25 '21 at 02:51
  • echo -e ${PATH//:/\\n} is problematic. With a path element of /my\tdir it will expand the \t into a TAB. With a single path element of just /usr/bin/* it will treat it as a glob pattern in bash unless you turn off globbing with set -f or set -o noglob. I suggest echo "${PATH//:/$'\n'}". Successfully tested in bash, zsh, and ksh93. Note: ANSI-C quoting is not POSIX (but neither is echo -e). You could also replace the $'\n' with a literal newline. – Robin A. Meade Jul 25 '21 at 03:31
5

Here's a quick way with bash

OLDIFS=$IFS IFS=: arr=($PATH) IFS=$OLDIFS
printf "%s\n" "${arr[@]}"
iruvar
  • 16,725
  • 4
    You can avoid saving/restoring IFS by using a subshell. e.g. (IFS=: arr=($PATH); printf "%s\n" "${arr[@]})") – Nick Jun 20 '13 at 18:39
  • 1
    That will also fail to display an empty entry in most shells if it's last (/bin:/usr/bin: used to be a common value for $PATH) – Stéphane Chazelas Jun 20 '13 at 19:38
  • In can also fail if $PATH contains some wildcard characters (unlikely) – Stéphane Chazelas Jun 20 '13 at 19:53
  • As you specified bash anyway, you may use IFS=: read -a arr <<< "$PATH" for the splitting part. That way IFS's new value will be used only while executing the read, so no need to restore it. http://pastebin.com/7E7C4AcR – manatwork Jun 21 '13 at 06:36
  • Thanks all for the feedback. @manatwork, do you know why IFS's new value persists after array creation but not after executing read? – iruvar Jun 21 '13 at 13:14
  • From “The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described in Shell Parameters. These assignment statements affect only the environment seen by that command.” – Environment and “Arrays are assigned to using compound assignments of the form name=(value1 … valuen)” – Arrays in the Bash Reference Manual I understand that array assignment doesn't support setting env variables that way – manatwork Jun 21 '13 at 14:00
4

Expanding on Smith John's solution, this makes for a nice alias in your .bash_profile:

alias MyPath='echo -e ${PATH//:/\\n}'
2

Note that an unset PATH has a different meaning from an empty PATH. An empty PATH contains one empty element, and that means looking for executables in the current directory only, an unset PATH means to search for executables in a default list of directories (but note that on some systems, not every tool agrees on the content of that list)

In zsh:

if (($+PATH)); then
  echo "$#path element(s):"
  printf '%q\n' "$path[@]"
else
  echo "PATH unset"
fi

In POSIX shells:

if [ -n "${PATH+.}" ]; then
  (
    set -o noglob
    IFS=:
    set -- $PATH''
    echo "$# element(s):"
    printf '"%s"\n' "$@"
  )
else
  echo "PATH unset"
fi
0

I found sed more intuitive:

$ echo $PATH | sed 's/:/\n/g'
nocibambi
  • 101