awkの使い方

5 min read読了の目安(約5200字

文字列やファイルから、テキストを意図したように操作したり、抽出したい場面がある。

文字列に対してフィルタリングする操作は、「正規表現」を使うと簡単に行うことができる。

Unix/Linuxのawkもそのような操作を可能するコマンドだ。

awkは一つのプログラミング言語として考えてもらっても良いほど多様な操作を我々に提供してくれる。

awkは以下のようなシンタックスを持つ。(fileを受け取る場合のみであるが)

awk /pattern/ { actions }  filenames  

patternは、各行に対するパターンマッチで、actionは各行にどういう処理を適用するか(表示だとか、変数の足し算とか)である。

まずはじめに

ファイルの中身を表示

hoge.txtがあるとする。

hello
world
hello2
helle

awkを使って以下のコマンドで、ファイルの中身を表示できる。

awk '{print}' hoge.txt 

ファイルの中からある文字列を含む行を抽出

例えば、hoge.txtからhelloを含む行を抽出する場合は以下のように記述する。

awk '/hello/{print}' hoge.txt

結果

hello
hello2

/hello/の部分が抽出したい文字列パターンを指定しており、/で文字パターンを囲むことで指定している。

そのため、

awk '{print}' hoge.txt 

は、以下のようにも記述可能。//は抽出したい文字列パターンがないという表現である。

awk '//{print}' hoge.txt 

ワイルドカード

*はワイルドカードとして機能する。この場合、hが0回以上含まれ、2が一回含まれる行が表示される。

awk '/h*2/{print}' hoge.txt

結果

hello2

.も似たように使える。この場合、どこかの時点でhで始まり、lを途中に含む行が表示される。

awk '/h.l/' hoge.txt

結果

hello
hello2
helle

注意点としては、hogefugahogefugaという行があった場合、

awk '/h*a/' hoge.txt

を実行すると、hogefugaではなく、/h*aにマッチする最長の文字列パターンであるhogefugahogefugaが返される仕様になっている。

複数の、ある文字を含む行抽出

lwを含む行を抽出したい場合。

awk '/[lw]/' hoge.txt

結果

hello
world
hello2
helle

さらに、lwのあとに2がくる行を抽出した場合は以下。

awk '/[lo]2/' hoge.txt

結果

hello2

範囲指定による行抽出

  • [0-9] : 数字
  • [a-z] : アルファベット小文字
  • [A-Z] : アルファベット大文字
  • [a-zA-Z] : アルファベット
  • [a-zA-Z 0-9] : アルファベットか数字

先頭の文字列一致

hから始まる行を抽出。

awk '/^h/' hoge.txt

結果

hello
hello2
helle

末尾の文字列一致

dで終わる行を抽出。

awk '/d$/' hoge.txt
// awk '/ld$/' hoge.txt

結果

world

ファイルから抽出

テスト用のファイル

No	Name	Math Eng
1	Alice	90	80
2	Bob	60	100
3 	Carl	80	40

スペースなしで全列表示

awk '//{print $1 $2 $3}' score.csv 

結果

NoNameMath
1Alice90
2Bob60
3Carl80

スペースありで全列表示

awk '//{print $1, $2, $3}' score.csv 

結果

No Name Math
1 Alice 90
2 Bob 60
3 Carl 80

printfでformat

awk '//{printf "%d-%s\n",$1, $2}' score.csv 

結果

0-Name
1-Alice
2-Bob
3-Carl

比較演算子

food.csvを以下のように想定する。

No      Item_Name               Quantity        Price
1       Mango                    45           $3.45
2       Apple                     25           $2.45	**
3       Pineapple                 5            $4.45	**
4       Tomato                   25           $3.45	**
5       Onion                     15           $1.45	**
6       Banana                    30           $3.45	**

比較演算子を使うsyntaxは以下。

expression { actions; }

expressionの部分に、望む式を記述する。

Quantityが10以下の行のみを表示する

awk '$3 < 10 {print $0}'

結果

3       Pineapple                 5            $4.45

Quantityが20以上の行にmanyとつける

awk '$3 >= 20 {printf "%s\t%s\n", $0, "many";} $3 < 20 {print $0; }' food.csv

結果

No      Item_Name               Quantity        Price	many
1       Mango                    45           $3.45	many
2       Apple                     25           $2.45	many
3       Pineapple                 5            $4.45
4       Tomato                   25           $3.45	many
5       Onion                     15           $1.45
6       Banana                    30           $3.45	many

Item名がAで始まる行を抽出

awk '($2 ~ /^A/) {printf "%s\n", $0}' food.csv

結果

2       Apple                     25           $2.45

Item名がAで始まり、Quantityが20以上の行を抽出

awk '($2 ~ /^A/) && ($3 >=20)  {printf "%s\n", $0}' food.csv

結果

2       Apple                     25           $2.45

30の場合を試してみる。

awk '($2 ~ /^A/) && ($3 >=30)  {printf "%s\n", $0}' food.csv

結果なし

nextコマンドで実行効率を上げる

「Quantityが20以上の行にmanyとつける」のところで紹介したコマンドでは、一つ問題がある。

awk '$3 >= 20 {printf "%s\t%s\n", $0, "many";} $3 < 20 {print $0; }' food.csv

3列目の値が20以上と確認したあとでも、それ以降の式 $3 < 20 {print $0; }が実行される点である。
そこで、 nextコマンドを使う。

awk '$3 <= 20 { printf "%s\t%s\n", $0,"*" ; next; } $3 > 20 { print $0 ;}' food.csv

BEGINとEND

BEGINENDというキーワードを使うことで、awkコマンドの実行時に合わせたアクションの実行ができる。

BEGINパターンは、入力行が読み込まれる前に、AwkがBEGINで指定されたアクションを一度だけ実行する。

ENDパターンは、Awkが実際に終了する前に、ENDで指定されたアクションを実行する。

以下のコマンドは、使用例である。food.csvのQuantityの行で20を超える行数をカウントするものである。

awk 'BEGIN { printf "Count: " } $3 >= 20 {n+=1} END {print n}' food.csv

結果

Count: 5

もしENDをつけない場合

awk '$3 >= 20 {n+=1} {print n}' food.csv

結果

1
2
3
3
4
4
5

awk 'BEGIN {print "hello world"}'

計算もできる。

awk 'BEGIN {print 1*2*100, 5/2}' // 200 , 2.5

組み込み変数

awkには組み込み変数がある。

  • FILENAME : current input file name( do not change variable name)
  • FR : number of the current input line (that is input line 1, 2, 3… so on, do not change variable name)
  • NF : number of fields in current input line (do not change variable name)
  • OFS : output field separator (出力時のの区切り文字)
  • FS : input field separator (入力時のの区切り文字)
  • ORS : output record separator (出力時のの区切り文字)
  • RS : input record separator (入力時のの区切り文字)

参考: https://www.tecmint.com/awk-built-in-variables-examples/

awk '{print NF}' food.csv

結果: 各行の列数が表示される。

4
4
4
4
4
4
4

参考