5

I have seen this code in .cshrc init files on a few machines. I went through a few awk tutorials in trying to understand how it works, but I am still unable to decrypt it.

setenv PATH `echo $PATH | awk 'NF&&\\!x[$0]++' RS='[:|\n]' | paste -sd:`

What does it do?

2 Answers 2

6

Doesn't work for me with the backslashes but I can explain this one to you:

echo "$PATH" | awk 'NF && !x[$0]++' RS='[:|\n]'

The record separator (RS) is set to one of the characters ":", "|" and newline. $PATH is usually just one line with elements separated by ":". This makes awk behave like the paths were not separated by ":" but each on its own line.

NF means that empty lines (NF == 0) are ignored. x is an associative array with the paths as subscript. !x[$0]++ means that the "line" is ignored if x[$0] is greater than 0. The result is that every line is output just once. During the first run x[$0] is increased so that in the following runs !x[$0] is false.

This example shows the frequency of all elements after the last line has been processed:

echo "a:b:a:c:a:b" |
  awk 'NF && !x[$0]++;END {for (var in x) print var ": " x[var]}' RS='[:|\n]'
a
b
c
a: 3
b: 2
c: 1
2
  • I just checked with OpenBSD awk (nawk) and gawk and in the both cases backslashes cause syntax error. May 18, 2013 at 23:03
  • 1
    @PredragPunosevac: I think the backslashes are there to prevent csh from doing history expansion on the exclamation mark.
    – Thor
    May 19, 2013 at 9:51
2

As described by Hauke the intention here is to only have unique elements in the $PATH variable.

This is not a portable awk script though, RS is often restricted to only a single character and not a regular expression. A more portable alternative would be something like this:

setenv PATH `printf "%s" "$PATH" | awk '{ sub("/$","") }; x[$0]++ < 1' RS=: | paste -s -d : -`

Tested in tcsh with gawk and nawk.

A few things to note:

  • the extraneous newline is avoided by using printf.
  • the !, which means history expansion to tcsh, can be replaced by checking if the value is less than 1.
  • terminating path separators are removed with sub().
2
  • 1
    With tcsh, you can do set -f path=($path) though (f for first). May 19, 2013 at 21:05
  • @StephaneChazelas: That is indeed a much better choice, I didn't know about that option. Thanks.
    – Thor
    May 19, 2013 at 21:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .