Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question
add comment

2 Answers 2

up vote 6 down vote accepted

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
share|improve this answer
    
I just checked with OpenBSD awk (nawk) and gawk and in the both cases backslashes cause syntax error. –  Predrag Punosevac May 18 '13 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 '13 at 9:51
add comment

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().
share|improve this answer
1  
With tcsh, you can do set -f path=($path) though (f for first). –  Stéphane Chazelas May 19 '13 at 21:05
    
@StephaneChazelas: That is indeed a much better choice, I didn't know about that option. Thanks. –  Thor May 19 '13 at 21:17
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.