awkの使い方
文字列やファイルから、テキストを意図したように操作したり、抽出したい場面がある。
文字列に対してフィルタリングする操作は、「正規表現」を使うと簡単に行うことができる。
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
が返される仕様になっている。
複数の、ある文字を含む行抽出
l
かw
を含む行を抽出したい場合。
awk '/[lw]/' hoge.txt
結果
hello
world
hello2
helle
さらに、l
かw
のあとに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
BEGIN
とEND
というキーワードを使うことで、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
Discussion