2016/07/10

uniq でフィールド指定をする方法

uniqで特定のフィールドに対して重複するものを除去するには、
オプション -f と -w を使えばよい。

フィールドは、スペースかタブで区切られた文字列。
-wは比較に使用するフィールドのバイト数。
-fが行頭から順に取り除くフィールドの数。

取り除いたフィールドとその次のフィールドとの間のスペースまたはタブは取り除かれない
そのため、-wと組み合わせるときは、-wのバイト数の計算に注意が必要である。

[a.txt]
11 abc ABC
12 abc xXx
234  abb  XXX
456 bbb XXX
234 bbbc YYY

2個目のフィールドの重複を取り除くには、
$ uniq -f1 -w5 a.txt
11 abc ABC
234  abb  XXX
456 bbb XXX
234 bbbc YYY
とする。-w5は、a.txtの5行目の2番目のフィールドとその直前のセパレータを合わせたバイト数が5であるため。

uniqではフィールド幅が可変長のフィールドの重複除去は行いにくい。
仮に-w6にすると、
$ uniq -f1 -w6 a.txt
11 abc ABC
12 abc xXx
234  abb  XXX
456 bbb XXX
234 bbbc YYY
のようになる。これは、比較に使用される文字が
 abc A
 abc x
  abb 
 bbb X
 bbb c
となるため(行頭にスペースが1個入っていることに注意)。

したがって、
[b.txt]
11 abc ABC
12 abc DEF
13 abcd xXx
14 abcde XXX
15 abcde YYY
に対して、2番目のフィールドで重複除去をしようとしても、
$ uniq -f1 -w6 b.txt
11 abc ABC
12 abc DEF
13 abcd xXx
14 abcde XXX
のようになってしまう。これを避けるには、対象とするフィールドを
行末にコピーしてuniqを行ったあと、それを除去すればよい。
例えば、
$ awk '{print $0" "$2}' b.txt | uniq -f3 | sed 's/ [[:graph:]]*$//'
11 abc ABC
13 abcd xXx
14 abcde XXX
のようにする。

日本語も処理できるが、-wがバイト数のため指定しにくい。
[c.txt (UTF-8)]
11 abc ABC
12 abc DEF
13 あさ xXx
14 あさ XXX
15 あみ YYY

例えば、2番目のフィールドの幅を4バイトとすると、
$ uniq -f1 -w4 c.txt
11 abc ABC
13 あさ xXx
となり、「あ」しか比較対象にならない。
なので、
$ awk '{print $0" "$2}' c.txt | uniq -f3 | sed 's/ [[:graph:]]*$//'
11 abc ABC
13 あさ xXx
15 あみ YYY
のように、後ろに移動させてからuniqするのが簡単。