AWKとは、文字列の検索、抽出、変換及び置き換えに長けたプログラミング言語です。おもにテキストファイルから一部分を抜き出したり、書式を変換するのに使われます。
たいていのLinuxには、あらかじめawkがインストールされている。ただし、awkより高機能なGNU版awk「gawk」をインストールすることもできる。
Microsoft WindowsではGNU版awkであるgawkを使用できる。gawkのインストール方法を示す。
awkはファイルを走査し、指定したパターンのいずれかと一致する行を探します。一致する行が見つかったときに指定されたアクション(動作) を実行します。
実行形式には、スクリプトを引数として指定する方法と、スクリプトを格納したファイルの名前を引数で指定する方法があります。
スクリプトを引数として指定する場合、シェルからawkコマンドを次のように実行します。
awk 'スクリプト' [ 入力ファイルのパス ]
入力ファイルのパスは省略可能です。省略した場合は、標準入力が対象となります。
awkコマンドには次のオプションを指定できる。
あらかじめスクリプトを格納したテキストファイルを作成しておき、そのスクリプトファイルの名前を引数で指定する方法の場合、シェルからawkコマンドを次のように実行する。
awk -f スクリプトファイルのパス [ 入力ファイルのパス ]
スクリプトファイルの拡張子にとくに決まりはない。
入力のフィールド区切り文字を指定する。
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
$ awk -F: '{print $3, $1}' /etc/passwd
0 root
1 daemon
2 bin
gawkコマンドはGNU版のAWKである。LinuxやWindowsなどさまざまなOSで使うことができる。
スクリプトをgawkコマンドの引数に指定して実行する場合の構文は次のとおり。
gawk [options] [--] 'program-text' [file ...]
あらかじめスクリプトを記述したファイルを用意しておき、gawkコマンドの引数にそのファイルパスを指定して実行する場合の構文は次のとおり。
gawk [options] -f program-file [--] [file ...]
program-text にはawkのプログラムを指定する。
gawkに指定できるオプションを示す。
$ gawk -f example.awk example.csv
スクリプトはテキストの置換・抽出・変換などのルールを指定するもので、パターン文とアクション文から成り立ちます。スクリプトは次の形式で記述します。
pattern { action }
入力行をパターンと比較して一致したら、アクション文を実行します。 パターンは正規表現で記述することができます。
awk '/井上/ { print $0 }' 住所録
上記の例では、住所録ファイル内で「井上」という文字列を含む行を出力しています。
パターンは次の3種類で表します。
任意の文字列を正規表現で表すことができます。
/RegularExpression/
行の末尾は $ で表します。たとえば、最後が na で終わる行は na$ と表せます。
$ cat students.txt
Shiratori Sana
Nonaka Kokona
Tanaka Miku
Yagi Miki
Sato Neo
Todaka Miko
Nozaki Yume
Kimura Sakia
$ awk '/na$/ { print $0 }' member
Shiratori Sana
Nonaka Kokona
$
特別なパターンとして、BEGINがあります。BEGINパターンのアクションには、最初の行を読み込む前に実行する処理を記述します。
BEGIN {
print "*** start ***"
}
{
print $0
}
特別なパターンとして、ENDがあります。ENDパターンのアクションには、最後の行を読み込んだ後に実行する処理を記述します。
{
print $0
}
END {
print "*** end ***"
}
awkコマンドにおけるデフォルトの区切り文字はスペース(空白文字)である。
区切り文字を指定するには、次に示す2通りの方法がある。
awkにはあらかじめ定義されている組み込み変数がある。
組み込み変数 | 説明 | デフォルト値 |
---|---|---|
FILENAME | 現在の入力ファイル名 | "-" |
RS | 入力のレコード区切り文字 | 改行(\n) |
FS | 入力のフィールド区切り文字 | スペース |
ORS | 出力のレコード区切り文字 | 改行(\n) |
OFS | 出力のフィールド区切り文字 | スペース |
NR | 現在のレコード数 | |
NF | 現在のレコードのフィールド数 | |
$0 | 現在の入力レコード | |
$n | 現在の入力レコードにおけるn番目のフィールド |
awkにおける変数の命名規則は次のとおり。
変数に値を代入するには「=」を使う。
BEGIN {
x = 10
y = "foo"
print x
print y
}
awkでは配列を使うことができる
BEGIN {
z[1] = 1
z[2] = 2
print z[1]
print z[2]
}
awkでは連想配列を使用できる。
$ cat order.txt
apple 10
grape 3
orange 5
apple 7
orange 3
$ cat order.awk
{ order[$1] += $2 }
END {
for (x in order) print x " " order[x]
}
$ gawk -f order.awk order.txt
apple 17
orange 8
grape 3
$
AWKには次表に示す組み込み関数がある。
関数 | 説明 |
---|---|
cos | 余弦(コサイン)を返す。 |
exp | 指数を返す。 |
getline | 次のレコードを読み込む。 |
index | 指定した文字列が最初に出現する位置を返す。 |
int | 小数点以下を切り捨てた整数を返す。 |
length | 文字列の長さ(文字数)を返す。 |
log | 自然対数を返す。 |
match | 指定した正規表現が最初に出現する位置を返す。 |
sin | 正弦(サイン)を返す。 |
split | 文字列を配列要素に分解して、その要素数を返す。 |
sprintf | 書式に従って文字列に変換する。 |
sqrt | 平方根を返す。 |
substr | 部分文字列を返す。 |
余弦(コサイン)を返す関数です。
number cos(number)
指数を返す関数です。
number exp(number)
exp関数の使用例を以下に示す。
$ cat naturalnumber.txt
1
2
3
4
5
$ awk '{print $1, exp($1)}' naturalnumber.txt
1 2.71828
2 7.38906
3 20.0855
4 54.5982
5 148.413
number getline()
現入力ファイルの次のレコードを組み込み変数 $0 に設定します。getline 関数は正常終了時には1を、ファイルの終わりに達すると0を、エラー発生時には-1を返します。
number index(string, substring)
文字列 string の中で文字列 substring が最初に出現する位置を返します。出現しなければ0を返します。
小数点以下を切り捨てた整数を返す関数です。
number int(number)
number int(string)
引数で指定した文字列の長さ(文字数)を返す。
number length([string])
自然対数を返す関数です。
number log(number)
log関数の使用例を以下に示す。
$ cat naturalnumber.txt
1
2
3
4
5
$ awk '{print $1, log($1)}' naturalnumber.txt
1 0
2 0.693147
3 1.09861
4 1.38629
5 1.60944
awk
match(string, regexp)
gawk
match(string, regexp[, array])
文字列stringの中で正規表現regexpが最初に出現する位置(1~)を戻り値として返す。regexpにマッチする文字列が存在しなければ戻り値として0を返す。
number sin(number)
number の正弦(サイン)を返す関数です。
number split(string, array, fs)
文字列 string を array[1]、array[2]、...、array[n] の配列要素に分割して、値 n を返します。この分割は正規表現fs によって行われ、fs が指定されていない場合はフィールドセパレータによって行われます。
$ cat split.txt
foo:bar baz:qux
$ cat split1.awk
{
n = split($0, a)
print n
for (i in a) {
print a[i]
}
}
$ awk -f split1.awk split.txt
2
foo:bar
baz:qux
$ cat split2.awk
{
n = split($0, a, ":")
print n
for (i in a) {
print a[i]
}
}
$ awk -f split2.awk split.txt
3
foo
bar baz
qux
$
string sprintf(format, expr, expr, ...)
format で指定した printf 形式に従って式の書式を定め、文字列を返します。
number sqrt(number)
number の平方根を返す関数です。/p>
sqrt関数の使用例を以下に示す。
$ cat naturalnumber.txt
1
2
3
4
5
$ awk '{print $1, sqrt($1)}' naturalnumber.txt
1 1
2 1.41421
3 1.73205
4 2
5 2.23607
string substr(string, m[, n])
文字列 string 内の m 番目から始まる長さ n の部分文字列を返します。
文字列を小文字に変換する。
tolower(string)
tolower関数の使用例を次に示す。
$ cat authors.txt
Charles Dickens
William Shakespeare
$ awk '{print tolower($0)}' authors.txt
charles dickens
william shakespeare
文字列を大文字に変換する。
toupper(string)
toupper関数の使用例を次に示す。
$ cat authors.txt
Charles Dickens
William Shakespeare
$ awk '{print toupper($0)}' authors.txt
CHARLES DICKENS
WILLIAM SHAKESPEARE
awkではユーザが独自の関数を定義することができる。関数の定義は次の書式で記述する。
function name([parameter[,parameter...]]) {
statements
return expr
}
awkにおける関数の引数は値渡しである。参照渡しにすることはできない。ただし、awkの変数はすべてグローバル変数なので、関数内で設定した変数を関数の外で参照できる。また、関数の外で設定した変数を関数内で参照できる。
関数の呼び出しは次の書式で記述する。
name([argument[,argument...]])
awkの関数は再帰呼び出し(関数の中でその関数自身を呼び出す)も可能である。
比較演算子 | 説明 |
---|---|
expr1 == expr2 | expr1とexpr2が等しい |
expr1 != expr2 | expr1とexpr2が等しくない |
expr1 < expr2 | expr1がexpr2より小さい |
expr1 <= expr2 | expr1がexpr2以下 |
expr1 > expr2 | expr1がexpr2より大きい |
expr1 >= expr2 | expr1がexpr2以上 |
論理演算子 | 説明 |
---|---|
expr1 && expr2 | 論理積 |
expr1 || expr2 | 論理和 |
!expr | 否定 |
アクションは一連の文です。使用できる文は次のうちのいずれかです。
コマンド | 説明 |
---|---|
break | if、while、do、for の処理を中止する。 |
continue | 現在の繰り返し処理を中断して、次の繰り返し処理にスキップする。 |
delete | 配列のキーと値を削除する。 |
do | 反復処理(後判定) |
exit | 残りの入力を飛ばして、ENDアクションを実行する。 |
for | 反復処理 |
if | 条件分岐 |
next | 現在の入力レコードの処理を終了して、次の入力レコードに進む。 |
標準出力に出力する。 | |
printf | 書式にしたがって文字列に変換し、標準出力に出力する。 |
while | 反復処理(前判定) |
ifは条件分岐を行う制御構文である。
if (condition) then-body [else else-body]
conditionには条件式を指定する。条件式には比較演算子や論理演算子を使うことができる。
if (x % 2 == 0)
print "xは偶数です。"
else
print "xは奇数です。"
改行を入れずに1行で記述することもできる。ただし、この場合はthen-bodyとelseをセミコロンで区切る必要がある。
if (x % 2 == 0) print "xは偶数です。"; else print "xは奇数です。"
AWKのwhile制御文は、条件が真の間、処理を繰り返す。
while (condition) statement
while (condition) { statements }
AWKのdo制御文は、処理を実行して、条件が真であればさらに処理を繰り返す。
do statement while (expression)
do { statements } while (expression)
AWKのfor制御文は、変数を初期化して、条件が真の間、処理を繰り返す。次の繰り返し処理を行う際、変数の値を変化させることができる。
for (initialization; condition; increment) statement
for (initialization; condition; increment) { statements }
for (var in array) statement
for (var in array) { statements }
for (i in a) {
delete a[i]
}
printfを用いたAWKのサンプルを示す。
$ cat factorial.txt
1
3
5
$ cat factorial.awk
{
f = 1
for (i = 2; i <= $1; i++) {
f = f * i
}
print $1 "の階乗は" f
}
$ awk -f factorial.awk factorial.txt
1の階乗は1
3の階乗は6
5の階乗は120
$
while、do、for の繰返し処理を中止する。
break
現在の繰り返し処理を中断して、次の繰り返し処理にスキップする。
continue
標準出力に出力する。
print [expr [expr]...] [>|>> filename]
print [expr [,expr]...] [>|>> filename]
expr を空白で区切って複数指定した場合、連結されて出力される。たとえば、
print "a" "b" "c"
は
print "abc"
と同じである。
expr をカンマで区切って複数指定した場合、空白で区切られて出力される。たとえば、
print "a", "b", "c"
は
print "a b c"
と同じである。
ファイルfoo.txtへ出力する例を示す。
print "Hello, world!" > foo.txt
ファイルfoo.txtへ追加出力する例を示す。
print "Hello, world!" >> foo.txt
printfは、書式にしたがって文字列に変換し、標準出力に出力する。
printf format [,expr...] [>|>> filename]
メタ文字 | 説明 |
---|---|
%c | ASCIIの1文字 |
%d | 整数 |
%nd | n桁の整数 |
%0nd | n桁の整数(桁数がnに満たない場合は0で埋める) |
%f | 浮動小数点 |
%e | 指数形式 |
%o | 符号なし8進数 |
%s | 文字列 |
%ns | n桁の文字列 |
%% | 「%」という文字そのもの |
$ cat printf.txt
65 foo
$ cat printf.awk
{printf "%c %d %04d %f %e %o %s %%", $1, $1, $1, $1, $1, $1, $2}
$ awk -f printf.awk printf.txt
A 65 0065 65.000000 6.500000e+01 101 foo %
$
next
現在の入力レコードの処理を終了して、次の入力レコードに進む。
残りの入力を飛ばして、ENDアクションを実行する。ENDアクションの中でのexitステートメントでは、さらにENDアクションが呼び出されることはない。
exit [ 終了ステータス ]
deleteステートメントは、配列のキーと値を削除する。
delete array[expr]
for (i in a) {
delete a[i]
}
組み込み変数 $0 は、入力行(レコード)を表します。
組み込み変数 $n は、n番目のフィールドを表します。
最初の2フィールドを逆順に出力する例を次に示す。
$ cat students.txt
Shiratori Sana
Nonaka Kokona
Tanaka Miku
Yagi Miki
Sato Neo
Todaka Miko
Nozaki Yume
Kimura Sakia
$ awk '{ print $2, $1 }' students.txt
Sana Shiratori
Kokona Nonaka
Miku Tanaka
Miki Yagi
Neo Sato
Miko Todaka
Yume Nozaki
Sakia Kimura
組み込み変数 FILENAME は、現入力ファイル名を表します。
$ awk '{print FILENAME}' foo.txt
foo.txt
$
組み込み変数 FS は、各レコードにおけるフィールドの区切り文字 (field separator) を正規表現で表したものである。
FS の初期値は空白文字である。
入力ファイル /etc/passwd からユーザIDとユーザ名を出力する例を示す。
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
$ awk 'BEGIN{FS=":"}{print $3,$1}' /etc/passwd
0 root
1 daemon
2 bin
組み込み変数 NF は、現レコード中のフィールド数を表します。
組み込み変数NRは、現レコード番号を表します。
特別パターン END においては現レコード番号は最後のレコード番号となりますので、つまり全レコード数(入力ファイルの行数)となります。
行番号と入力行を出力するawkスクリプトの例を示す。
$ cat students.txt
Shiratori Sana
Nonaka Kokona
Tanaka Miku
Yagi Miki
Sato Neo
Todaka Miko
Nozaki Yume
Kimura Sakia
$ awk '{ print NR, $0 }' students.txt
1 Shiratori Sana
2 Nonaka Kokona
3 Tanaka Miku
4 Yagi Miki
5 Sato Neo
6 Todaka Miko
7 Nozaki Yume
8 Kimura Sakia
2番目のフィールドを合計して、合計値と平均値を出力するAWKのサンプルを示す。
$ cat foo.txt
foo 30
bar 50
baz 100
$ cat foo.awk
{ sum += $2 }
END {
print "合計値", sum
print "平均値", sum / NR
}
$ awk -f foo.awk foo.txt
合計値 180
平均値 60
$
組み込み変数 OFMT は、数値の出力形式を表します。
組み込み変数 OFS は、出力フィールドセパレータを表します。
組み込み変数 ORS は、出力レコードセパレータを表します。
組み込み変数 RS は、入力レコードのセパレータ(区切り文字)を表す。
CSVの1行目にあるヘッダを読み飛ばすには、NR が2以上のパターンに対してアクションを起こすようにする。
NR>1 {
# 処理
}