業務でCSVをごにょごにょする必要があったのでawkコマンドを学習してみた。
awkコマンドとは
AWKはパターンとアクションを組み合わせるプログラミング言語です。
引用元:「シェル芸」に効く!AWK処方箋
「パターンにマッチした場合はアクションを実行する」という処理ができる。
使い方として最もメジャーなのは、テキストの中からパターンに一致する箇所のみ抽出したり、CSVファイルの中から特定の列だけ抜き出したり、、みたいなことだろうか。
使ってみる
$ echo a b c | awk '{print}' a b c
この場合、printが「アクション」にあたる。パターンは省略されている
$ echo a b c | awk '{print $0}' a b c
出力結果は一緒だが、パターンを省略しない場合はこのようになる。
「$0」というのは渡された文字列全体を表す。「a b c」をスペースで区切ったものを先頭から$1, $2, $3...と参照することができ、$0は文字列全体を参照できる。
Rubyなどの配列のインデックスのように、先頭が0から始まるわけではないので注意。
$ echo a b c | awk '{print $1}' a
$ echo a b c | awk '{print $1, $2}' a b
ここでprint $1, $2
としているにもかかわらず「a, b」ではなく「a b」と出力されているのは、出力時の区切り文字がデフォルトで空白になっているから。
出力時の区切り文字を指定
組込変数OFS
に代入する。
$ echo a b c | awk -v OFS="," '{print $1, $2}' a,b
$ echo a b c | awk -v OFS="***" '{print $1, $2}' a***b
入力時の区切り文字を指定
デフォルトでは空白が区切り文字として判断されるので
$ echo a,b,c | awk '{print $1, $2}' a,b,c
これだとうまく行かない。
渡されたテキストをカンマ区切りで分割したい場合は-F
オプションで指定する
$ echo a,b,c | awk -F , '{print $1, $2}' a b
組込変数OFS
と組み合わせて、入力時も出力時もカンマで統一する場合
$ echo a,b,c | awk -F , -v OFS="," '{print $1, $2}' a,b
こうなる。若干複雑になってきた
テキストファイルに対しての処理
テキストファイルを対象にすることもできる。
$ cat users.csv id,name,age 1,hoge,24 2,fuga,34
このようなCSVがあったときに
$ cat users.csv | awk -F , -v OFS="," '{print $1, $2}' id,name 1,hoge 2,fuga
こんなこともできる。
$ awk -F , -v OFS="," '{print $1, $2}' users.csv id,name 1,hoge 2,fuga
パイプでつなげず対象ファイルを後ろに持ってきてもOK。
if文による条件マッチ
awkコマンドの中でも色々と関数があり、if文とかも使えるので
$ cat users.csv | awk '{if(NR!=1) print $0}' 1,hoge,24 2,fuga,34
こんなこともできる。
NR
というのはawkの組込変数で、現在処理中の行数が格納されている。
if(NR!=1) print $0
は「一行目じゃなかったら列全体をprintせよ」なので、結果的にCSVファイル最上の列名以外を全抽出することができる。