A solution - not one that is as elegant as those that change the *RS variables, but perhaps reasonably clear:
PATH=`awk 'BEGIN {np="";split(ENVIRON["PATH"],p,":"); for(x=0;x<length(p);x++) { pe=p[x]; if(e[pe] != "") continue; e[pe] = pe; if(np != "") np=np ":"; np=np pe}} END { print np }' /dev/null`
The entire program works in the BEGIN and END blocks. It pulls your PATH variable from the environment, splitting it into units. It then iterates over the resulting array p (which is created in order by split()
). The array e is an associative array that is used to determine whether or not we've seen the current path element (e.g. /usr/local/bin) before, and if not, is appended to np, with logic to append a colon to np if there is already text in np. The END block simply echos np. This could be further simplified by adding the -F:
flag, eliminating the third argument to split()
(as it defaults to FS), and changing np = np ":"
to np = np FS
, giving us:
awk -F: 'BEGIN {np="";split(ENVIRON["PATH"],p); for(x=0;x<length(p);x++) { pe=p[x]; if(e[pe] != "") continue; e[pe] = pe; if(np != "") np=np FS; np=np pe}} END { print np }' /dev/null
Naïvely, I believed that for(element in array)
would preserve order, but it doesn’t, so my original solution doesn’t work, as folks would get upset if someone suddenly scrambled the order of their $PATH
:
awk 'BEGIN {np="";split(ENVIRON["PATH"],p,":"); for(x in p) { pe=p[x]; if(e[pe] != "") continue; e[pe] = pe; if(np != "") np=np ":"; np=np pe}} END { print np }' /dev/null