とほほのAWK入門

トップ > とほほのAWK入門

目次

AWKとは

Hello world

Hello world を表示するプログラムは次のようになります。拡張子は特に決まりはありません。

{ print "Hello world!" }

AWK は grepsed のようなフィルタコマンドとして実装されているため、何らかの入力が必要です。下記の様に実行します。

$ echo | awk -f hello.awk

下記のように、コマンドラインにプログラムを直接指定する方式もよく利用されます。

$ echo | awk '{ print "Hello world!" }'

簡単な利用例

簡単な利用例をいくつか紹介します。ちょっとした便利ツールとして重宝できそうってことがわかるかと思います。

# ls -l の結果から所有者とグループとファイル名だけを表示する
$ ls -l | awk '{ print $3, $4, $9 }'
yamada yamada hello.awk
yamada yamada sample.awk

# ファイル名がhello.awkのものだけ表示する
$ ls -l | awk '$9=="hello.awk" { print $3, $4, $9 }'
yamada yamada hello.awk

# /etc/passwdを:をデリミタとしてユーザ名とホームディレクトリのみを表示する
$ cat /etc/passwd | awk -F: '{ print $1, $6 }'
root /root
bin /bin

# /etc/passwdのユーザ名とホームディレクトリを整形して表示する
cat /etc/passwd | awk -F: '{ printf("%-8s %s\n", $1, $6) }'
root    /root
bin     /bin

# df実行結果から2行目以降の第2カラムの合計を表示する
$ df | awk 'NR>=2 {z+=$2}; END {print z}'
31097704

実行方法

プログラムはコマンドラインに直接指定するか、-f でプログラムファイル名を指定します。入力ファイルは標準入力、または引数としてファイル名を渡します。

$ cat data.txt | awk '{ print $1, $2 }'		# プログラムを直接指定
$ cat data.txt | awk -f sample.awk		# プログラムファイルで指定
$ awk -f sample.awk data1.txt data2.txt		# 入力データを引数で指定

プログラムファイルの先頭を #!/usr/bin/awk -f とすることで、プログラムファイルをコマンドとして直接実行することができます。

#!/usr/bin/awk -f
{ print $1, $8 }
$ chmod 755 sample.awk
$ ps -ef | ./sample.awk

レコードとフィールド

入力データはレコードとフィールドに分割されます。レコードは行、フィールドは空白文字で区切られたカラムに相当します。レコードとフィールドの区切り文字は ビルトイン変数 RSFS で変更することができます。

field1  field2  field3		# record1
field1  field2  field3		# record2
field1  field2  field3		# record3

レコード全体は $0、フィールドは $1, $2, $3, ... で参照することができます。

{ print $0 }			# レコード全体を表示
{ print $1, $2, $3 }		# 第1、第2、第3フィールドを表示

基本文法

基本的な構文は下記の様になります。標準入力、または、引数で指定されたファイルを読み込み、パターンにマッチした行に対してアクションを実行します。

pattern1 { action1 }	# pattern1にマッチしたらaction1を実行
pattern2 { action2 }	# pattern2にマッチしたらaction2を実行
    :

pattern を省略するとすべての行に対して action を行います。

{ action }		# すべての行に対してactionを実行

{ action } を省略すると、{ print $0 } (その行を表示する)をアクションとして実行します。

/ABC/			# /ABC/ { print $0 } と同じ

パターン

パターンには /.../ で正規表現を記述できます。

/ABC/ { ... }			# レコードの中にABCという文字列が含まれていればマッチ

if (...) 文の ... に記述するような条件式を記述することもできます。

$1 == "ABC" { ... }		# 第1フィールドがABCであればマッチ

条件式で正規表現を使用することもできます。~ は正規表現にマッチしたらという演算子です。

$1 ~ /^#/ { ... }		# 第1フィールド先頭が#で始まっていたらマッチ

条件1, 条件2 を記述した場合、条件1が真になる行から、条件2が真になる行までマッチします。

$1=="START", $1=="END" { ... }	# 第1フィールドがSTARTである行から、ENDである行までマッチ

BEGIN, END はプログラムの一番最初と一番最後にアクションを実行します。

BEGIN { ... }			# 最初に1度だけアクションを実行
END { ... }			# 最後に1度だけアクションを実行

BEGINFILE, ENDFILE は各入力ファイルの開始時と終了時にアクションを実行します。

BEGIN { ... }			# 最初に1度だけアクションを実行
END { ... }			# 最後に1度だけアクションを実行

アクション

アクションには、print で値を出力したり、if, while などで制御したりなど、通常のスクリプト言語と同様の処理を記述することができます。

{
    if ($1 == "D") {			# 第1フィールドの値が"D"であれば
        print $2, $3			# 第2、第3フィールドの値を出力する
    }
}

セミコロン(;)を使用すると、1行に複数の文を記述することができます。

{
    a = 1; b = 2; c = 3;
    print a, b, c
}

コメント

# から行末まではコメントです。

# Comment...
{
    print "Hello "       # Comment...
    print "world!"       # Comment...
}

演算子

下記の演算子を使用できます。

(...)			# グルーピング
$num			# 第nフィールドの値

+num			# プラス(正数)
-num			# マイナス(負数)

expr1 + expr2		# 加算
expr1 - expr2		# 減算
expr1 * expr2		# 乗算
expr1 / expr2		# 除算
expr1 % expr2		# 剰余
expr1 ^ expr2		# expr1expr2
expr1 ** expr2		# expr1expr2

expr++			# exprをインクリメント(インクリメント前の値が式の値)
++expr			# exprをインクリメント(インクリメント後の値が式の値)
expr--			# exprをデクリメント(デクリメント前の値が式の値)
--expr			# exprをデクリメント(デクリメント後の値が式の値)

expr1 == expr2		# expr1expr2と等しければ
expr1 != expr2		# expr1expr2と等しくなければ
expr1 < expr2		# expr1expr2より小さければ
expr1 <= expr2		# expr1expr2以下であれば
expr1 > expr2		# expr1expr2より大きければ
expr1 >= expr2		# expr1expr2以上であれば

str ~ reg		# 文字列strが正規表現regにマッチすれば
str !~ reg		# 文字列strが正規表現regにマッチしなければ

!bool			# boolが真でなければ(NOT)
bool1 && bool2		# bool1かつbool2が真であれば(AND)
bool1 || bool2		# bool1またはbool2が真であれば(OR)

var = expr		# varexprを代入
var += expr		# var = var + exprと同じ
var -= expr		# var = var - exprと同じ
var *= expr		# var = var * exprと同じ
var /= expr		# var = var / exprと同じ
var %= expr		# var = var % exprと同じ
var ^= expr		# var = var ^ exprと同じ
var **= expr		# var = var ** exprと同じ

cond ? expr1 : expr2	# condが真であればexpr1、さもなくばexpr2

配列

変数名[添え字] で配列を扱うことができます。

foo[1] = 123
foo[2] = "ABC"
print foo[1]		# => 123
print foo[2]		# => ABC

添え字には文字列を使用することもできます。

foo["name"] = "Yamada"
foo["age"] = 26
print foo["name"]	# => Yamada
print foo["age"]	# => 26

配列の個数は length() で求めることができます。数値を添え字とする配列は次のようにしてループを回します。

foo[0] = "ABC"
foo[1] = "DEF"
for (i = 0; i < length(foo); i++) {
    print foo[i]
}

添え字が文字列の場合は次のようにしてループを回します。

foo["name"] = "Yamada"
foo["age"] = 26
for (x in foo) {
    print x "=" foo[x]		# => name=Yamada, age=26
}

配列の中に該当の要素があるか否かを調べるには下記の様にします。

foo["name"] = "Yamada"
if ("name" in foo) {
    print "Exist"
}

配列要素を削除するには delete を用います。

foo["name"] = "Yamada"
foo["age"] = 26
delete foo["name"]	# foo["name"]を削除
delete foo		# 配列foo自体を削除

多次元配列

次のようにして多次元配列を扱うことができます。

foo[1, 1] = 1001
foo[1, 2] = 1002
foo[2, 1] = 2001
foo[2, 2] = 2002
for (i = 1; i <= 2; i++) {
    for (j = 1; j <= 2; j++) {
        print foo[i, j]
    }
}

多次元配列のように見えますが、内部的には添え字を SUBSEP("\034") で連結した1次元配列として扱っています。

foo["A", "B"] = "XYZ"
print foo["A" SUBSEP "B"]	# => XYZ
print foo["A\034B"]		# => XYZ

配列の配列

下記は、配列の配列を使用する例です。

foo[1][1] = 1001
foo[1][2] = 1002
foo[2][1] = 2001
foo[2][2] = 2002
for (i in foo) {
    for (j in foo[i]) {
        print foo[i][j]
    }
}

print文

print は変数や値を出力します。

print $1, $2, $3

値をカンマ(,)で連結するとビルトイン変数 OFS に指定された出力フィールドセパレータ(デフォルトは半角空白文字)で区切って出力します。

a = "AA"; b = "BB"; c = "CC";
print a, b, c			# => AA BB CC

OFS を変更することで区切り文字を変更することができます。

a = "AA"; b = "BB"; c = "CC";
OFS = ","
print a, b, c			# => AA,BB,CC

値をカンマ(,)で区切らない場合は文字列の連結が行われた後に出力されます。

a = "AA"; b = "BB"; c = "CC";
print a b c			# => AABBCC

printf文

printf は、値をフォーマットして出力します。

printf "%d\n", 123		# 整数値を出力
printf "%f\n", 3.14		# 実数値を出力
printf "%s\n", "ABC"		# 文字列を出力

%d などは引数の型を示します。下記のものを使用できます。

printf "d: %d\n", 123		# => 123 (10進整数)
printf "i: %i\n", 123		# => 123 (10進整数)
printf "u: %u\n", 123		# => 123 (符号無し10進整数)

printf "x: %x\n", 123		# => 7b (16進整数)
printf "X: %X\n", 123		# => 7B (16進整数)
printf "o: %o\n", 123		# => 173 (8進整数)

printf "f: %f\n", 123.45	# => 123.450000 (小数表記)
printf "F: %F\n", 123.45	# => 123.450000 (小数表記)
printf "e: %e\n", 123.45	# => 1.234500e+02
printf "E: %E\n", 123.45	# => 1.234500E+02
printf "g: %g\n", 1.2345e10	# => 1.2345e+10 (%f表記と%g表記を自動判断)
printf "G: %G\n", 1.2345e10	# => 1.2345E+10 (%f表記と%g表記を自動判断)

printf "c: %c\n", 0x41		# => A (ASCIIコード)
printf "s: %s\n", "ABC"		# => ABC (文字列)

printf "%%: %%\n"		# => % (%を文字として出力)

下記の様に桁数を指定して出力することができます。

printf "|%7d|\n", 123		# |    123| ... 7桁
printf "|%+7d|\n", 123		# |   +123| ... 正の場合に+を表示
printf "|%-7d|\n", 123		# |123    | ... 左寄せ7桁
printf "|%07d|\n", 123		# |0000123| ... 0埋め7桁

printf "|%.2f|\n", 3.1416	# |3.14| ... 小数部2桁
printf "|%7.2f|\n", 3.1416	# |   3.14| ... 7桁(小数部2桁)

printf "|%7s|\n", "ABC"		# |    ABC| ... 右寄せ7桁
printf "|%-7s|\n", "ABC"	# |ABC    | ... 左寄せ7桁

リダイレクト

print や printf の出力を標準出力ではなくファイルに書き込むことができます。

print "ABC" > "data.txt"	# ファイルdata.txtに新規書き込み
print "ABC" >> "data.txt"	# ファイルdata.txtに追記書き込み

print や printf の出力をコマンドにパイプ(|)で渡すこともできます。下記の例では ps -axu の結果を受け取り、VSZ($5) と COMMAND($11) の値をソートして表示します。

$ ps -axu | awk '{ print $5, $11 | "sort -n" }'

getline文

getline [var]
getline [var] < file
command | getline [var]
command |& getline [var]

getline は標準入力やファイルからデータを読み取ります。下記では標準入力から変数 x にデータを読み取り、printf で出力しています。

BEGIN {
    printf "Input: "
    getline x
    printf "[%s]\n", x
}

変数を省略すると、$0, $1, $2, ... に読み取ります。

getline
printf "[%s] [%s] [%s] [%s]\n", $0, $1, $2, $3

下記の様にしてファイルからデータを読み取ることもできます。

BEGIN {
    while ((getline < "xx.dat") > 0) {
        printf "[%s] [%s] [%s] [%s]\n", $0, $1, $2, $3
    }
}

下記の様に | を用いることで、外部コマンドの実行結果からデータを読み取ることもできます。

BEGIN {
    cmd = "ps -ef"
    while (cmd | getline) {
        printf "[%s] [%s] [%s] [%s]\n", $1, $2, $3, $8
    }
}

|& を用いると外部コマンドにデータを渡し、その実行結果を読み出すことが可能となります。(双方向接続)

BEGIN {
    cmd = "base64"
    print "AAA" |& cmd
    close(cmd, "to")
    while ((cmd |& getline) > 0) {
        printf "[%s]\n", $0
    }
    close(cmd)
}

if文

if文は、もし、... であればという条件を記述します。

if (n == 3) {			# もしnが3と等しければ
    print "OK"			# OKを表示
}

else は、さもなくば、を意味します。

if (n == 3) {			# もしnが3と等しければ
    print "OK"			# OKを表示
} else {			# さもなくば
    print "NG"			# NGを表示
}

アクションが1文であれば { や } は省略することができます。正確に言うと、if はひとつの文しか実行しないので、複数の文を実行するには { ... } で囲って複文にします。これは、while や do-while 文でも同様です。

if (n == 3)
    print "OK"
else
    print "NG"

while文

while文は、条件が成り立っている間、アクションを繰り返して実行します。

n = 0
while (n < 10) {		# nが10より小さい間
    print n			# nを表示して
    n++				# nをインクリメントする
}

do-while文

do-while文も、条件が成り立っている間、アクションを繰り返して実行します。while文が、条件をチェックしてからアクションを実行するのに対して、do-while文はアクションを実行してから条件をチェックするため、最初から条件が成り立っていなくても最低1回はアクションが実行される点が異なります。

n = 0
do {
    print n
    n++
} while (n < 10)

for文

for文は、n回処理を繰り返すといった場合に使用します。下記の例は10回繰り返し実行する例です。まず、i = 0 の初期化を行い、print n のアクションを実行し、i++ で i をインクリメントし、これを i < 10 の条件が成り立つ間繰り返します。

for (i = 0; i < 10; i++) {		# iが0~9の間
   print n				# nを出力する
}

for文はまた、配列に対して繰り返し処理を行う場合にも使用されます。

arr1[0] = "Red"
arr1[1] = "Green"
arr1[2] = "Blue"
for (i in arr1) {
    print arr[i]
}

arr2["red"] = "#f00"
arr2["green"] = "#0f0"
arr2["blue"] = "#00f"
for (x in arr2) {
    print x " = " arr2[x]
}

switch文

switch文は、複数の条件分岐を行う際に使用します。case で条件を指定します。下記の例では、変数 color の値が何なのかによって処理を振り分けています。default はどの条件にもマッチしなかった場合のアクションを指定します。

switch (color) {		# coloeの値が...
case "red":			# "red"であれば
    print "#f00"		# "#f00"を表示
    break
case "green":			# "green"であれば
    print "#0f0"		# "#0f0"を表示
    break
case "blue":			# "blue"であれば
    print "#00f"		# "#00f"を表示
    break
default:			# いずれでもなければ
    print "(unknown)"		# "(unknown)"を表示
    break
}

break文を記述しない場合、次の条件のアクションも実行します。下記の場合、"red" や "green" の場合の break 文が無いため、"red"、"green"、または "blue" の場合に "(known)" を表示します。

switch (color) {		# colorの値が
case "red":			# "red"でも
case "green":			# "green"でも
case "blue":			# "blue"でも
    print "(known)"		# "(known)"を表示
    break
default:
    print "(unknown)"
    break
}

break文

break文は、switch文で使用する他、for、while、do-while文のループを抜ける場合に使用します。

for (i = 0; i < 10; i++) {
    if (i == 5) {			# iが5になったら
        break				# forループを終了する
    }
    print n
}

for や while が多重にネストしている場合、一番内側のループを抜けます。

for (i = 0; i < 10; i++) {
    for (j = 0; j < 10; j++) {
        if (i == 5 && j == 5) {		# iが5、jも5になったら
            break			# 一番内側のjのループを終了する
        }
        print i, j
    }
}

continue文

continue文は、一番内側のループを繰り返し実行します。下記の例では i が 3 の時はアクションを途中でスキップして、i = 4 のループに移ります。

for (i = 0; i < 5; i++) {
    if (i == 3) {			# iが3であれば
        continue			# 後続のアクションをスキップする
    }
    print i				# => 0 1 2 4
}

next文

next文は、現在のレコードに対するアクションを終了し、次のレコードの処理に移ります。

{
    if (NF < 5) {			# フィールドの個数(NF)が5未満であれば
        next				# 次のレコードの処理に移る
    }
}

nextfile文

nextfile文は、現在のファイルに対するアクションを終了し、次のファイルの処理に移ります。

{
    if ($1 == "END") {			# 第1フィールドが"END"であれば
        nextfile			# 次のファイルの処理に移る
    }
}

exit文

exit文はプログラムを終了させます。引数にはプログラムの終了ステータスを指定することができます。

if ($1 == "END") {			# 第1フィールドが"END"であれば
    exit 1				# プログラムを終了する(終了ステータス=1)
}

関数

いくつかの ビルトイン関数 の他、function を用いてユーザ定義の関数を使用することができます。下記は、第1引数 x と第2引数 y を受け取り、その和を返却する関数 add() を定義し、$1 と $2 の合計を表示しています。return は関数の戻り値を指定します。

function add(x, y) {
    return x + y
}
BEGIN {
    print add($1, $2)
}

function の代わりに省略形の func を使用することもできます。

func add(x, y) {
    return x + y
}

ローカル変数を使用するには、引数の箇所にローカル変数を記述します。引数とローカル変数の間は慣習的に少し離して記述します。

function sum(arr,  i, ret) {
    ret = 0;
    for (i = 0; i < length(arr); i++) {
        ret = ret + arr[i]
    }
    return ret
}
BEGIN {
   arr[0] = 123
   arr[1] = 234
   arr[2] = 300
   print sum(arr)
}

@ を用いると、文字列を関数名として使用することができます。関数名を関数の引数として受け取り、呼び出す際に便利です。

fname = "add"
print @fname(3, 5)			# => 8

正規表現

/ ... / で正規表現を使用することができます。正規表現の詳細は とほほの正規表現入門 を参照してください。

/ABC/ { ... }			# 行全体の中にABCが含まれていれば
$0 ~ /ABC/ { ... }		# 行全体の中にABCが含まれていれば
$1 ~ /ABC/ { ... }		# 第1カラムにABCが含まれていれば
$1 !~ /ABC/ { ... }		# 第1カラムにABCが含まれていなければ
{ if ($1 ~ /ABC/) { ... } }	# 第1カラムにABCが含まれていれば

下記のエスケープシーケンスを利用できます。

\\		# バックスラッシュ
\a		# ベル(BEL) (0x07)
\b		# バックスペース(BS) (0x08)
\f		# フォームフィード(FF) (0x0c)
\n		# 改行(LF) (0x0a)
\r		# 復帰(CR) (0x0d)
\t		# タブ(TAB) (0x09)
\v		# 垂直タブ(VT) (0x0b)
\nnn		# 文字コード指定(8進数)
\xhh		# 文字コード指定(16進数)
\/		# スラッシュ(/)
\"		# ダブルクォート(")

下記の正規表現を使用できます。

^		# 行頭にマッチ
$		# 行末にマッチ
.		# 任意の1文字(改行を含む)にマッチ
[ABC]		# AまたはBまたはCにマッチ
[A-Z]		# A~Zにマッチ
[a-zA-Z0-9]	# 半角英数字にマッチ
[^ABC]		# A, B, C以外の文字にマッチ
A|B		# AまたはBにマッチ
(...)		# グルーピングを行う
A*		# 0個以上のAにマッチ
A+		# 1個以上のAにマッチ
A?		# 0個または1個のAにマッチ
A{n}		# n個のAにマッチ
A{n,}		# n個以上のAにマッチ
A{n, m}		# nm個のAにマッチ

[...] の中では下記の POSIX表現を使用することができます。

[:alnum:]	# 英数字(a-zA-Z0-9)
[:alpha:]	# 英字(a-zA-Z)
[:lower:]	# 小文字(a-z)
[:upper:]	# 大文字(A-Z)
[:digit:]	# 10進数字(0-9)
[:xdigit:]	# 16進文字(0-9a-fA-F)
[:blank:]	# 空白文字(SP,TAB)
[:space:]	# 空白文字(SP,TAB,LF,CR,FF,VT)
[:cntrl:]	# 制御文字
[:graph:]	# 印字可能文字(空白を含まない)
[:print:]	# 印字可能文字(空白を含む)
[:punct:]	# 記号文字(!"#$%&'-=^~\|@`...)

下記を使用することもできます。

\s		# 空白文字(TAB,LF,CR,FF,VT)
\S		# \s以外の文字
\w		# アンダーバーを含む英数字
\W		# \w以外の文字
\<		# 単語の先頭
\>		# 単語の末尾
\y		# 単語の先頭または末尾
\B		# 単語の先頭または末尾以外
\`		# 文字列の先頭
\'		# 文字列の末尾

正規表現(/.../)は通常はオブジェクトとして扱われないため、変数に代入しても正規表現として使用することはできません。これを実現するためには /.../ の代わりに @/.../ を使用します。

reg = @/^[A-Z]+$/
if (str ~ reg) { ... }

インクルード(@include)

@include は外部ファイルをプログラムファイルとして読み取ります。ファイルを探す場所は通常カレントディレクトリですが、AWKPATH が指定されていればその場所から探します。

@include "mysubr.awk"

ライブラリロード(@load)

@load は共有ライブラリ形式のライブラリを読み込みます。

@load "ordchr";
BEGIN { print chr(0x41) }

ネームスペース(@namespace)

AWK 5.0 からはネームスペースの機能が加わりました。同じ関数名であっても、下記の様に名前空間を分けて呼び出すことが可能です。

# mylibA.awk
@namespace "mylibA"
function myfunc(x, y) { return x + y }
# mylibB.awk
@namespace "mylibB"
function myfunc(x, y) { return x * y }
@include "mylibA.awk"
@include "mylibB.awk"
BEGIN {
    print mylibA::myfunc(3, 5)		# mylibA.awkのmyfunc()
    print mylibB::myfunc(3, 5)		# mylibB.awkのmyfunc()
}

コマンドラインオプション

コマンドラインには下記のオプションや引数を指定できます。

-F fs (--field-separator)
フィールドセパレータを指定します。
-f program-file (--file)
プログラムファイルを指定します。複数指定することもできます。
-v var=value (--assign)
プログラムから参照可能な変数を引数で指定します。
var=value
プログラムから参照可能な変数を引数で指定します。「awk '{print foo}' foo=FOO」 の様に、入力ファイルリストを指定する場所に記述する必要があります。
--
-- より後ろに記述した - や -- で始まる文字列を、オプションではない引数として扱います。
-
入力ファイルリストとして - を指定すると、標準入力を読み込みます。例えば、file1 - file2 は、file1、標準入力、file2 の順番で読み込みます。
-W opttion
gawk特有のオプションを指定(詳細別途)します。
-b (--characters-as-bytes)
入力をシングルバイト文字として扱います。
-c (--traditional)
古いBWK awk(nawk)との互換モードで実行します。
-C (--copyright)
コピーライトを表示します。
-dfile (--dump-variables=file)
グローバル変数をファイルに書き出します。file を省略した場合は awkvars.out ファイルに書き出します。
-Dfile (--debug=file)
デバッグコマンドをファイルから読み取ります。
-e program-file (--source)
プログラムを引数で指定します。-f と組み合わせることで、プログラムをファイルと引数の両方で指定できます。
-E file (--exec)
var=value 形式のオプションを無視します。AWK プログラムを CGI として動作させる場合に、URL で指定された変数が悪影響を及ぼさないよう無視する場合に指定します。
-g (--gen-pot)
プログラムを国際化対応させるために _"..." のようなテキストを gettext が扱える形式で標準入力に出力します。
-h (--help)
ヘルプを表示します。
-i library-file (--include)
ライブラリ的なプログラムファイルを読み込みます。-f とほぼ同じですが、読み込み済みファイルは二重には読み込まないこと、-i で指定したライブラリファイル以外にメインのプログラムを求めることの2点が異なります。
-l extension (--load)
gawk の拡張ライブラリを読み込みます。拡張ライブラリは AWKLIBPATH (/usr/lib64/gawk など) に格納される共有ライブラリとして提供されています。
-Lvalue (--lint)
AWK プログラムのエラーや警告を調べます。value には fatal(警告をエラーとして表示), invalid(不正なもののみ警告), no-ext(拡張ライブラリについては警告しない)のいずれかを指定します。
-M (--bignum)
多倍長精度数値計算ライブラリ(MP, MPFR)が利用可能な場合、それを使用します。
-n (non-decimal-data)
printf("%d", ...) で出力する際に、"0123" を8進数、"0x123" を16進数の数値とみなして扱います。将来バージョンでは削除される可能性があります。
-N (use-lc-numeric)
小数点として扱う文字を、ピリオド(.)ではなく、ロケールに従った文字、例えばフランス語の場合はカンマ(,)を使用するようにします。
-ofile (--pretty-print=file)
プログラムを整形して file に書き出します。file を省略すると awkprof.out に書き出します。
-O (--optimize)
gawk がプログラムを最適化することを許可します。過去互換のために残されていますが、現在の gawk はデフォルトで最適化を行います。
-pfile (--profile=file)
プロファイル情報を file に書き出します。どのアクションが何回実行されたかが記録されます。file を省略すると awkprof.out ファイルに書き出します。
-P (--posiz)
POSIX 互換モードで動作します。
-r (--re-interval)
正規表現の中で {n, m} の使用を許可します。過去互換のために残されており、現在の gawk ではデフォルトで使用可能です。
-s (--no-optimize)
プログラムの最適化を禁止します。
-S (--sandbox)
安全性のため、system(), getline の入力リダイレクション、print や printf の出力リダイレクション、および、ダイナミックエクステンションの利用を禁止します。また、ARGV に引数で指定したファイルを追加しません。
-t (--lint-old)
オリジナル版の awk (Version 7 Unix) で利用できないを使用している場合に警告を出します。
-V (--version)
gawk のバージョン情報を表示します。

ビルトイン変数

いくつかのビルトイン変数を使用することができます。下記の例ではフィールドセパレータ(FS) をカンマ(,)としてCSV ファイルを読み込み、そのフィールド数(NF)を出力しています。

$ awk '{ print NF }' FS=, data.csv
$ awk -v FS=, '{ print NF }' data.csv
$ awk 'BEGIN { FS="," } { print NF }' data.csv

ビルトイン変数には下記のものがあります。

FS
入力時のフィールドセパレータ(Field Separator)。コマンドラインオプションの -F でも変更することができます。デフォルトは空白文字(" ")です。空白文字の場合は特別に、ひとつ以上の空白文字・タブ文字・改行文字が区切り文字となります。空文字("")にすると、1文字1文字がフィールドとして区切られます。"[A-Z]" の様な正規表現を指定することもできます。
RS
入力時のレコードセパレータ(Record Separator)。デフォルトは改行文字("\n")です。
OFS
出力時のフィールドセパレータ(Output Field Separator)。デフォルトは空白文字(" ")です。print $1, $2, $3 のようにカンマ(,)を使用した場合、OFS の値が区切り文字として出力されます。
ORS
出力時のレコードセパレータ(Output Record Separator)。デフォルトは改行文字("\n")です。
FILENAME
現在処理中の入力ファイル名。
NF
現在行のフィールド数(Number of Fields)。
NR
現在行のレコード番号(Number of Records)。
FNR
現在のファイルにおけるレコード番号。複数のファイルを処理する際、NR はリセットされませんが、FNR はリセットされます。
ARGC
引数の個数(ARGuments Count)。
ARGV
引数の配列(ARGuments Vector)。
ARGIND
引数のインデックス(ARGuments INDex)。複数の入力ファイルを処理する際に対象のファイルに応じて変動します。ARGV[ARGIND] は FILENAME と同値となります。
ENVIRON
環境変数の配列(ENVIRONments)。ENVIRON["PATH"] で環境変数 PATH の値を参照することができます。
OFMT
print で実数を出力する際のフォーマット(Output FORmat)。デフォルトは "%.6g" です。
CONVFMT
実数を文字列に変換する際のフォーマット(CONVert FORmat)。デフォルトは "%.6g" です。print num だと print の引数なので OFMT が参照されますが、print "[" num "]" だと文字列連結となるので CONVFMT が参照されます。
IGNORECASE
正規表現やセパレータのマッチングで大文字・小文字を無視するか否か。初期値は 0 で無視しません。0 以外の値が設定されると大文字・小文字を区別しなくなります。
SUBSEP
多次元配列の添え字の区切り文字(SUBscript SEParator)。デフォルトは "\034" (FS:\x1C)。foo["A", "B"] は foo["A\034B"] と同じ扱い。
ERRNO
エラー番号(ERRor Number)。getline のリダイレクションや読み込み、close() 実行などでエラーが発生した場合にエラー番号が設定されます。
RLENGTH
match 関数でマッチした部分文字列の長さ。match("ABCDEF", /DEF/) の場合 3 となります。
RSTART
match 関数でマッチした部分文字列の最初の文字の位置(最初の文字が1)。match("ABCDEF", /DEF/) の場合 4 となります。
RT
レコードを読み込んだ際に区切り文字として使用されたセパレータを示します。例えば RS="[/|]" と指定されていた場合、レコードが / で区切られた場合は / が、| で区切られた場合は | が格納されます。
FIELDWIDTHS
入力データを固定長データとして読み込みます。例えば FIELDWIDTHS="3 4 5" とした場合、入力データは、3文字、4文字、5文字の固定長フィールドのデータとして読み込まれます。
BINMODE
入出力をバイナリモードに切り替えます。0 はテキストモード(初期値)、1 や "r" は読み込みのみ、2 や "w" は書き込みのみ、3 や "rw" や "wr" は読み書き両方をバイナリモードにします。主に Windows において \r\n と \n を自動変換したくない場合に使用します。
FPAT
FS がフィールドセパレータを正規表現で指定するのに対し、FPAT はフィールド自体を正規表現で示します。FPAT="[a-zA-Z]+" とした場合、1文字以上の英字の連続部分をフィールドとみなします。FPAT を指定した場合、FS や FIELDWIDTHS は無視されます。
LINT
0 や null 以外の値が設定された場合、--lint オプションが指定されたものとして動作します。。
PREC
小数点演算のビット数を指定します。通常は53ビット長演算ですが、PREC=100 を指定するとより正確な100ビット演算が行われます。-M オプションと合わせて指定します。「awk -M -v PREC=100 -v OFMT="%.100g" 'BEGIN { print 1/3 }'」
ROUNDMODE
未稿。
TEXTDOMAIN
プログラムを多国語対応する際に、翻訳ファイルを見つけるためのテキストドメインを指定します。デフォルトは "messages" です。詳細は gettext() などのマニュアルを参照してください。
FUNCTAB
利用可能な関数名の配列。
SYMTAB
利用可能な変数の配列。foo="FOO" とした場合、SYMTAB["foo"] が "FOO" になります。SYMTAB["foo"]="baa" とすると、foo も "baa" になります。
PROCINFO
PROCINFO["uid"](ユーザID)、PROCINFO["pid"](プロセスID)など、プロセスに関する情報の配列です。

ビルトイン関数

下記のビルトイン関数を使用できます。個々の関数の詳細説明は省略します。

# 数学関連
int(x)					# xの整数部
rand()					# 0.0~1.0の間の乱数
srand(x)				# 乱数の初期シードを指定(xを省略すると現時刻をシードとする)
sqrt(x)					# 0.0~1.0の間の乱数
exp(x)					# xの平方根
sin(x)					# xのサイン
cos(x)					# xのコサイン
atan2(y, x)				# y/xのアークタンジェント
log(x)					# log x

# 文字列関連
asort(arr[, dest[, how]])		# 値順にソート
asorti(arr[, dest[, how]])		# 添え字(インデックス)順にソート
gensub(reg, replace, how[, target])	# 文字列を高度に置換
gsub(reg, replace[, target])		# 文字列を連続置換
index(str, s)				# str中にsが出現する位置
length([str])				# strの長さ
match(str, reg[, array])		# strが正規表現regにマッチするかを検査
patsplit(str, array[, fpat[, seps]])	# strをパターンで分割
split(str, array[, fpat[, seps]])	# strをパターンで分割
sprintf(fmt, exp1, ...)			# 値をフォーマットに合わせて整形
strtonum(str)				# 文字列を数値に変換
sub(reg, repl[, target])		# 文字列を置換
substr(str, start[, length])		# strの一部を抽出
tolower(str)				# 小文字に変換
toupper(str)				# 大文字に変換

# 入出力関連
close(filename[, how])			# ファイルをクローズ
fflush([filename])			# 書き込みをフラッシュ
system(command)				# 外部コマンドを実行

# 時間関連
mktime(datespec[, utcflag])		# 文字列型時刻を数値型時刻に変換
strftime([fmt[, time[, utc]]])		# 数値型時刻を文字列型時刻に変換
systime()				# 現在の時刻を数値型時刻で取得

# ビット操作関連
and(x1, x2, ...)			# 論理積(AND)
or(x1, x2, ...)				# 論理和(OR)
xor(x1, x2, ...)			# 排他的論理和(XOR)
compl(x)				# 補数
lshift(x, n)				# 左シフト
rshift(x, n)				# 右シフト

# 型情報関連
isarray(x)				# 配列か否かを確認
typeof(x)				# 型名を取得

# 国際化対応関連
bindtextdomain(directory[, domain])		# ドメインのパスを設定
dcgettext(directory[, domain[, category]])	# 単一の参照に関するドメインを上書き
dcngettext(str1, str2, n[, domain[, category]])	# 単一の参照に関するドメインを上書き(複数)

ライブラリ関数

ビルトイン関数の他にも @load または -i や -l オプションで読み込むライブラリ機能があります。

@load "filefuncs"
chdir(directory)			# カレントディレクトリを移動
stat(file, statdata[, follow])		# ファイル情報を得る
flags = or(FTS_PHYSICAL, ...)		# fts()に渡すフラグを生成
fts(pathlist, flags, filedata)		# ディレクトリを横断検索する?

@load "fnmatch"
fnmatch(pattern, filename, flags)	# filenameがワイルドカードpatternにマッチするか調べる

@load "fork"
pid = fork()				# プロセスをフォークする
waitpid(pid)				# 指定した子プロセスの終了を待つ
wait()					# いずれかの子プロセスの終了を待つ

@load "inplace"				# -i inplaceで読み込むと入力ファイルを書き換える

@load "ordchr"
number = ord(string)			# 文字を文字コードに変換
char = chr(number)			# 文字コードを文字に変換

@load "readdir"				# ディレクトリ内のファイル一覧?(うまく動かない)
BEGIN { FS = "/" }
{ print "file name is", $2 }

@load "revoutput"			# "/dev/stdout"への書き込みを逆順にする
BEGIN { REVOUT = 1 }
{ print $0 > "/dev/stdout" }

@load "rwarray"
writea(file, arr)			# 配列をバイナリデータとしてファイルに格納する
reada(file, arr)			# writea()で書き込まれたデータを配列として読み出す

@load "readfile"
readfile(file)				# ファイルの内容を一括して読み込む
BEGIN { PROCINFO["readfile"] = 1 }	# $0とRTを設定するらしいがうまく機能しない

@load "time"
gettimeofday()				# 1970年1月1日00:00:00からの秒数を得る
sleep(sec)				# sec秒間スリープする

環境変数

下記の環境変数が参照されます。

AWKPATH
-f オプションで指定したプログラムファイルを探すディレクトリ名を指定します。複数のディレクトリを指定する場合はコロン(:)で連結します。
AWKLIBPATH
-l オプションで指定したライブラリファイルを探すディレクトリ名を指定します。複数のディレクトリを指定する場合はコロン(:)で連結します。
GAWK_MSEC_SLEEP
接続をリトライする間隔をミリ秒で指定します。
GAWK_READ_TIMEOUT
読み込みのタイムアウト時間をミリ秒単位で指定します。
GAWK_SOCK_RETRIES
TCP/IPソケットによる通信でリトライを行う回数を指定します。
POSIXLY_CORRECT
true や 1 を設定すると、POSIX互換モードで動作します。
AWKBUFSIZE
I/Oバッファサイズを指定します。POSIX互換モードの場合に有効です。
AWK_HASH
gst を設定すると、ハッシュアルゴリズムを標準のものから GNU Smalltalk 版の高速なものに切り替えます。
AWKREADFUNC
何かが設定されていると、プログラムファイルを1行ずつ読み込みます。これは、デバッグにおける問題を解決するために使用されます。
GAWK_MSG_SRC
何かが設定されていると、警告やエラーメッセージにファイル名と行番号の表示を加えます。
GAWK_LOCALE_DIR
多国語対応のためのメッセージファイルの場所を示します。
GAWK_NO_DFA
何かが設定されていると、DFA正規表現マッチングを行わなくなります。>/dd>
GAWK_STACKSIZE
スタックサイズを指定します。
INT_CHAIN_MAX
数値型配列管理に使用するハッシュチェーンのための最大アイテム数を指定します。
STR_CHAIN_MAX
文字列型配列管理に使用するハッシュチェーンのための最大アイテム数を指定します。
TIDYMEM
何かが設定されていると、メモリリークを検出するために mtrace() を呼ぶようになります。

ネットワークアクセス

AWK でネットワークアクセスすることも可能です。下記は簡単な TCP サーバの例です。

BEGIN {
    print "Hello!" |& "/inet/tcp/8080/0/0"
    close("/inet/tcp/8080/0/0")
}

次は簡単な TCP クライアントの例です。上記の TCP サーバに接続して Hello! メッセージを取得して表示します。

BEGIN {
    "/inet/tcp/0/localhost/8080" |& getline
    print $0
    close("/inet/tcp/0/localhost/8080")
}

次は簡単な HTTP クライアントの例です。

BEGIN {
    RS = ORS = "\r\n"
    http = "/inet/tcp/0/www.example.com/80"
    print "GET http://www.example.com/" |& http
    while ((http |& getline) > 0) print $0
    close(http)
}

Copyright (C) 2021 杜甫々
初版:2021年1月17日 最終更新:2021年1月17日
http://www.tohoho-web.com/ex/awk.html