I cannot figure out how to list the various paths in $PATH separately so that they look like this:
/bin
/usr/bin
/usr/local/bin
How can this be done?
I cannot figure out how to list the various paths in $PATH separately so that they look like this:
/bin
/usr/bin
/usr/local/bin
How can this be done?
You can do this with any one of the following commands, which substitutes all occurrences of : with new lines \n.
sed:
$ sed 's/:/\n/g' <<< "$PATH"
tr:
$ tr ':' '\n' <<< "$PATH"
python:
$ python -c 'import sys;print(sys.argv[1].replace(":","\n"))' "$PATH"
Use bash's Parameter Expansion:
echo "${PATH//:/$'\n'}"
This replaces all : in $PATH by a newline (\n) and prints the result. The content of $PATH remains unchanged.
If you only want to replace the first :, remove second slash: echo -e "${PATH/:/\n}"
Using IFS:
(set -f; IFS=:; printf "%s\n" $PATH)
IFS holds the characters on which bash does splitting, so an IFS with : makes bash split the expansion of $PATH on :. printf loops the arguments over the format string until arguments are exhausted.
We need to disable globbing (wildcard expansion) using set -f so that wildcards in PATH directory names don't get expanded.
Probably the only way that hasn't been mentioned is the way i've been using it for years:
echo $PATH | tr ":" "\n"
so, in your .profile or .bash_profile or whatever, you can add:
alias path='echo $PATH | tr ":" "\n"'
Since all the scripting languages have been taken already, I'll go with C. It's quite easy to get environment variables with get_env() function ( see GNU C Library documentation). The rest is simply character manipulation
bash-4.3$ cat get_path.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *path = getenv("PATH");
int length = strlen(path) -1;
for(int i=0;i<=length;i++){
if (path[i] == ':')
path[i] = '\n';
printf("%c",path[i]);
}
printf("\n");
return 0;
}
bash-4.3$ gcc get_path.c
bash-4.3$ ./a.out
/home/xieerqi/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
/opt/microchip/xc16/v1.25/bin
/opt/microchip/xc32/v1.40/bin
/opt/microchip/xc8/v1.35/bin
/home/xieerqi/bin
/home/xieerqi/bin/sh
But also because "why not" , here's alternative python version via command line arguments sys.argv
python -c 'import sys; print "\n".join(sys.argv[1].split(":"))' "$PATH"
Ruby doesn't come with Ubuntu by default, unlike C compiler and Python interpreter, but if you ever find yourself using it, the solution in Ruby would be this:
ruby -ne 'puts $_.split(":")' <<< "$PATH"
As suggested by 7stud (Thank you very much !) in the comments, this can also be shortened with
ruby -F: -ane 'puts $F' <<<$PATH
and this way
ruby -0072 -ne 'puts chomp' <<<$PATH
We can utilize split() function to break down the line read into array, and use for-each loop to print out each item on separate line.
awk '{split($0,arr,":"); for(var in arr) print arr[var]}' <<< $PATH
Here's the equivalent in Go:
$ cat path.go
package main
import (
"fmt"
"os"
"strings"
)
func main() {
for _, p := range strings.Split(os.Getenv("PATH"), ":") {
fmt.Println(p)
}
}
$ go run path.go
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
/home/nathan/.local/bin
/home/nathan/go/bin
Here are a few more approaches. I'm using a PATH with directories containing backslashes, spaces and even a new line to show that they should work with anything (except the cut one which fails on newlines):
$ echo "$PATH"
/bin:usr/bin/:/usr/local/bin:/some\ horrible thing:/even
new lines
Some Perl ways:
$ perl -pe 's/:/\n/g' <<<"$PATH"
/bin
usr/bin/
/usr/local/bin
/some\ horrible thing
/even
new lines
The -p means "print every input line after applying the script given by -e". The script is using the substitution operator (s/oldnew/) to replace all : with newlines.
$ perl -lne 'print for split /:/' <<<"$PATH"
/bin
usr/bin/
/usr/local/bin
/some\ horrible thing
/even
new lines
The -l adds a newline to each print call. Here, the script is splitting its input on : and then loops over each split element and prints it.
$ perl -F: -ane '$"="\n";print "@F"' <<<"$PATH"
/bin
usr/bin/
/usr/local/bin
/some\ horrible thing
/even
new lines
The -a makes perl behave like awk: it will split each of its input lines on the character given by -F (so :, here) and save the result in the array @F. The $" is a special Perl variable, the "list separator", whose value is printed between each element of a printed list. So setting it to a newline will make print @list print each element of @list and then a newline. Here, we're using it to print @F.
$ perl -F: -ane 'print join "\n", @F' <<<"$PATH"
/bin
usr/bin/
/usr/local/bin
/some\ horrible thing
/even
new lines
Same idea as above, just less golfed. Instead of using $", we are explicitly joining the array with newlines and then printing.
Simple grep with PCRE magic:
$ grep -oP '(^|:)\K[^:]+' <<<"$PATH"
/bin
usr/bin/
/usr/local/bin
/some\ horrible thing
/even
new lines
The -o makes grep print only the matching portion of each line, so each match is printed on a separate line. The -P enables Perl Compatible Regular Expressions (PCRE). The regex is looking for stretches of non-: ([^:]+) that follow either the beginning of the line (^) or a : character. The \K is a PCRE trick that means "discard anything matched before this point" and is used here to avoid printing the : as well.
And a cut solution (this one fails on newlines, but can deal with backslashes and spaces):
$ cut -d: -f 1- --output-delimiter=$'\n' <<<"$PATH"
/bin
usr/bin/
/usr/local/bin
/some\ horrible thing
/even
new lines
The options used are -d: which sets the input delimiter to :, -f 1- which means print all fields (from the 1st to the end) and --output-delimiter=$'\n' which sets the, well, output delimiter. The $'\n' is ANSI C quoting and is a way to print a newline character in the shell.
In all of the above examples, I am using bash's (and some other shells') here string (<<<) operator to pass a string as input to a program. So command <<<"foo" is equivalent to echo -n "foo" | command. Note that I am always quoting "$PATH", without the quotes, the shell would have eaten the newline character.
@7stud gave another approach in the comments that's just too good not to include:
$ perl -0x3a -l012 -pe '' <<<"$PATH"
That's what's known as golfing. The -0 specifies the input record separator as an octal or hexadecimal number. This is what defines a "line" and its default value is \n, a newline character. Here, we're setting it to a : which is x3a in hex (try printf '\x3a\n'). The -l does three things. First it removes the input record separator ($/) from the end of each line—effectively removing the : here—and second, it sets the output record separator ($\) to whatever octal or hex value it is given (012 is \n). If $\ is defined, it is added to the end of each print call, so this will result in a newline appended to each print.
The -pe will print each input line after applying the script given by -e. Here there is no script because all the work is done by the option flags as described above!
We need more Java!
public class GetPathByLine {
public static void main(String[] args) {
for (String p : System.getenv("PATH").split(":")) {
System.out.println(p);
}
}
}
Save this to GetPathByLine.java, and compile using:
javac GetPathByLine.java
Run with:
java GetPathByLine
┌─[17:06:55]─[kazwolfe@BlackHawk]
└──> ~ $ cat GetPathByLine.java
public class GetPathByLine {
public static void main(String[] args) {
for (String p : System.getenv("PATH").split(":")) {
System.out.println(p);
}
}
}
┌─[17:06:58]─[kazwolfe@BlackHawk]
└──> ~ $ javac GetPathByLine.java
┌─[17:07:02]─[kazwolfe@BlackHawk]
└──> ~ $ java GetPathByLine
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
Through awk.
echo $PATH | awk -F: '{for(i=1;i<=NF;i++)print $i}'
Through python.
$ echo $PATH | python3 -c 'import fileinput
for line in fileinput.input():
for i in line.split(":"):
print(i)'
Note that Indentation is very important in python.
Explanation for @Cyrus answer
echo "${PATH//:/$'\n'}"
Notes:
ANSI-C Quoting - it explains $'some\ntext'
Shell Parameter Expansion - it explains ${parameter/pattern/string}, If pattern begins with ‘/’, all matches of pattern are replaced with string.
So we have:
I use Stephen Collyer's "Bash Path Functions" (see his article in Linux Journal). It permits me to use the "colon separated list" as a datatype in shell programming. For example, I can produce a list of all the directories in the current directory by:
dirs="";for i in * ; do if [ -d $i ] ; then addpath -p dirs $i; fi; done
Then, listpath -p dirs produces a list.
How to display the paths in $PATH separately
These are my preferred ways of doing this based on my respective use-cases and concerns about compatibility and resource usage.
trFirst, if you need a quick, easy to remember, and readable solution, just echo PATH and pipe it to translate (tr) to turn the colons into newlines:
echo $PATH | tr : "\n"
It has the downside of using two processes because of the pipe, but if we're just hacking in a terminal, do we really care about that?
If you want a fairly permanent solution in your .bashrc for interactive use, you can alias the following command to path, but the readability of this solution is questionable:
alias path="echo \"${PATH//:/$'\n'}\""
If pattern begins with ‘/’, all matches of pattern are replaced with string. Normally only the first match is replaced.
The above command substitutes the colon with newlines using Bash's Shell Parameter Expansion:
${parameter/pattern/string}
To explain it:
# v--v---------delimiters, a'la sed
echo "${PATH//:/$'\n'}"
# ^^ ^^^^^----string, $ required to get newline character.
# \----------pattern, / required to substitute for *every* :.
Good luck remembering this when you're just hacking on the command line, if you haven't aliased it, though.
Alternatively, a fairly cross-compatible, readable, and understandable approach that doesn't rely on anything other than the shell is use the following function (I suggest in your .bashrc.)
The following function temporarily makes the Internal (or Input) Field Separator (IFS) a colon, and when an array is given to printf, it executes until the array is used up:
path () {
local IFS=:
printf "%s\n" ${PATH}
}
This method of function creation, IFS, and printf are provided for by posix, so it should work in most posix-like shells (especially Dash, which Ubuntu usually aliases as sh).
Should you use Python for this? You could. This is the shortest Python command I can think of for this:
python -c "print('\n'.join('${PATH}'.split(':')))"
or Python 3 only (and perhaps more readable?):
python3 -c "print(*'${PATH}'.split(':'), sep='\n')"
These should work in any usual shell environment as well, so long as you have Python.
This solution is simpler than the Java, C, go and awk solutions:
$ LPATH=$PATH wine cmd /c echo %LPATH::=$'\n'% 2> /dev/null
/usr/local/bin
/usr/local/sbin
/usr/bin
/usr/sbin
Here's another great possibility:
$ jrunscript -classpath /usr/share/java/bsh.jar -e 'print(java.lang.System.getenv("PATH").replaceAll(":","\n"))'
/usr/local/bin
/usr/local/sbin
/usr/bin
/usr/sbin
This would require some dependencies to be installed:
sudo apt-get install openjdk-8-jdk-headless bsh
Another AWK way is to treat each directory as a separate record, rather than as a separate field.
awk 'BEGIN{RS=":"} {print $0}' <<<"$PATH"
I find that syntax particularly intuitive. But, if you like, you can shorten it by making the print $0 implicit (it is the default action, and 1 evaluates to true, causing it to be done for every line):
awk 'BEGIN{RS=":"} 1' <<<"$PATH"
AWK's default input and output record separator is the newline (line break). By setting the input record separator (RS) to : before reading the input, AWK automatically parses a colon-deliminated $PATH into its constitutent directory names. AWK expands $0 to each whole record, the newline remains the output record separator, and no looping or gsub is needed.
ek@Io:~$ echo "$PATH"
/home/ek/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
ek@Io:~$ awk 'BEGIN{RS=":"} {print $0}' <<<"$PATH"
/home/ek/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
AWK is often used to parse records into separate fields, but there's no need for that just to construct a list of directory names.
This works even for input containing blanks (spaces and tabs), even multiple consecutive blanks:
ek@Io:~$ awk 'BEGIN{RS=":"} {print $0}' <<<$'ab\t\t c:de fg:h'
ab c
de fg
h
That is, unless you cause AWK to rebuild the record (see below), it's no problem to have spaces or tabs (the default field separators) in the input. Your PATH probably doesn't contain spaces on an Ubuntu system, but if it does, this will still work.
It's worth mentioning, as a side note, that AWK's ability to interpret a record as a collection of fields becomes useful for the related problem of constructing a table of directory components:
ek@Io:~$ awk -F/ 'BEGIN{RS=":"; OFS="\t"} {$1=$1; print $0}' <<<"$PATH"
home ek bin
usr local sbin
usr local bin
usr sbin
usr bin
sbin
bin
usr games
usr local games
snap bin
The curious $1=$1 assignment serves the purpose of forcing AWK to rebuild the record.
(This is probably more useful for cases where additional processing is to be done on the components, than for the exact example shown of simply printing the table.)
This works in Git Bash for Windows:
python -c "import os; print os.environ['PATH'].replace(';', '\n')"
It breaks the lines nicely:
c:\Rtools\mingw_32\bin
C:\Program Files (x86)\Intel\MPI-RT\4.1.3.045\ia32\bin
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\ProgramData\chocolatey\bin
C:\Program Files (x86)\vim\vim74