🦉

awkを使って2つのcsvファイルを比較する方法

2021/11/23に公開

はじめに

実はawkコマンドは複数のファイルを引数に取れます。(↓man awkより抜粋)

SYNOPSIS
awk [ -F fs ] [ -v var=value ] [ 'prog' | -f progfile ] [ file ... ]

こんな2つのファイルを用意。

first.txt
111
222
333
second.txt
444
555
666

awkに2つのファイルを引数に渡すと2つのファイルが連結されたように処理されます。

> awk '{print $0}' first.txt second.txt
111
222
333
444
555
666

awkにはいくつかビルトイン変数があって、ファイルを比較するときには NRFNR を使います。
NRにはファイルが連結された状態で連番が、FNRは各ファイルごとに連番が振られます。

> awk '{print $0}' first.txt second.txt
111   // NR = 1  FNR = 1
222   // NR = 2  FNR = 2
333   // NR = 3  FNR = 3
444   // NR = 4  FNR = 1
555   // NR = 5  FNR = 2
666   // NR = 6  FNR = 3

従って、NR==FNRを条件にすれば1ファイル目の行、NR!=FNRを条件にすれば2ファイル目の行だけを処理することが出来ます。

実際に比較してみる

例えばこんなcsvがあるとします。

before.csv
user_id,name,birthday,gender
1,aaa,20000101,male
2,bbb,19501201,female
3,ccc,19990707,female
4,ddd,20101010,female

このcsvのuser_idを+100したafter.csvを作成して、本当にafter.csvのuser_idがbefore.csvの+100になっているのかを最後に確認したい場合、diffコマンドだとuser_idが異なるので全行出力されてしまいます。
この例では数行程度なので大した手間ではないですが、何万行もあるファイルだったらawkで処理できると楽できます。

今回はあえてミスしたafter.csvを作成して、エラーを検知できるか検証します。

after.csv
user_id,name,birthday,gender
101,aaa,20000101,male
103,bbb,19501201,female // 2+100 -> 103のミス
103,ccc,19990707,female
104,ddd,20101010,female

このような処理を記述したファイルを作成しておきます。

compare.awk
NR!=1 && NR==FNR { mem[NR] = $1; next }
FNR!=1 && NR!=FNR && mem[FNR] != ($1 - 100) { print "error. Before user_id:",mem[FNR],",after user_id",$1,",line:",FNR}

awkは事前に処理をファイルに記述してそれを-fオプションで渡すことが出来ます。

実際に実行してみると、ちゃんとミスした行を出力してくれました。

> awk -F, -f compare.awk before.csv after.csv
error. Before user_id: 2 ,after user_id 103 ,line: 3

処理の詳細

一行目
NR!=1 && NR==FNR { mem[NR] = $1; next }
  • NR!=1:1行目(=ヘッダ)を除く
  • NR==FNR:前述の通り、1ファイル目
  • mem[NR] = $1:配列mem{NR}番目の要素に、1列目のデータ(=user_id)を格納
FNR!=1 && NR!=FNR && mem[FNR] != ($1 - 100) { print "error. Before user_id:",mem[FNR],",after user_id",$1,",line:",FNR}
  • FNR!=1:1行目(=ヘッダ)を除く
  • NR!=FNR:前述の通り、2ファイル目
  • mem[FNR] != ($1 - 100):1ファイル目のuser_idの配列の{FNR}番目の要素の値が、処理行の値から100引いた値と等しいか。つまり、1ファイル目と2ファイル目の同じ行目のuser_idの差分が100でない場合。

追記

awkについてはとほほさんのawk入門がすごく参考になります。

Discussion