awk - テキストデータのパターン処理を行う

スポンサーリンク

awkはパターンマッチやテキスト処理が得意なプログラミング言語です。awkはプログラムのコンパイルが必要ないインタプリタ言語になります。

awkコマンドは、テキストデータである入力ファイルを行ごと処理することができ、一行で記述できる程度のプログラム量でも様々な処理を行うことができます。また、awkはインタプリタ言語であるため、PerlやRuby、Pythonのようにシバン(#!)を用いれば、自己完結型のスクリプトを作成することができます。

スポンサーリンク

awkプログラムについて

awkプログラムは基本的にパターン(pattern)アクション(action)で成り立つルールを記述していきます。awkが入力ファイルから行を読み込んだときに当てはまるルールの処理を行います。ルールは基本的に以下のように記述できます。

 

また、awkプログラムはルールのほかに関数を定義する場合もあります。関数の構文は以下のように記述できます。

 

また、awkプログラムの文は改行もしくはセミコロン(;)で区切ることができます。

また、上のプログラムは入力ファイルの行を出力した後、次の行に3を出力します。なので、入力ファイルの全ての行の後に3という数字が挿入される結果が得られます。

コマンド例と実行結果

 

また、awkのコメント行は'#'を利用します。'#'を利用した場合は行末までコメントになります。

 

 

 

パターンがないルール

パターンがないルールは、awkが行を読み込むときに常に処理されます。

testdata.txt

コマンド例と実行結果

 

$0,$1,$2,...は特別な変数になります。$0の場合は行の全体を表します。
$1,$2,...の変数は行がスペースやタブで区切られたデータ列とみて、1列目、2列目、...を表します。
この区切り文字は組み込み変数FSで変更できます。例えば、testdata.txtがcsvのようなファイルの場合は

または、awkコマンドのFオプションを用いて、

のようにすることで同様な結果を得ることができます。

 

 

 

アクションがないルール

アクションがないルールにはデフォルトのアクションが設定されています。デフォルトのアクションは、

になります。

testdata.txt

コマンド例と実行結果

 

また、BEGINやENDのような特別なパターンにはデフォルトのアクションはありません。

 

 

 

基本的な特別なパターン

awkプログラムのパターンにはBEGINとENDの特別なパターンがあります。

BEGINはawkが処理し始める最初の処理を記述できます。主に、変数の初期化などを行うことができます。

例えば、以下は行番号を出力するコマンド例になります。

fruits.txt

コマンド例と実行結果

 

行番号を表示する場合は、もっと簡単に記述できます。組み込み変数NRはawkが処理した行数が設定されています。このNRを利用すると

のように同様の結果を得ることができます。

 

同様に、ENDはawkの処理が終了するときの最後の処理を記述できます。
例えば、以下は行を逆順にする例になります。

コマンド例と実行結果

 

また、他の特別なパターンにBEGINFILEとENDFILEがあります。これはファイルの読み込み開始と読み込み終了の処理を記述できます。

コマンド例と実行結果

 

awkコマンドの引数の入力ファイル'-'は、標準入力になります。

 

 

 

awkプログラムの実行

awkコマンドは、awkのプログラムファイルを用いない場合、

のように利用できます。そして、inputfileがない場合は標準入力から読み込まれます。

awkコマンドでinputfileがないときにパイプやリダイレクトを用いていない場合、端末から入力を読み込むために入力待ちの状態になります。これはCtrl+dで入力を終了できます。ただし、BEGINパターンの文の場合は、端末からの入力を行わずに終了します。

awkのプログラムをファイルから実行させたい場合は、-fオプションを用います。

また、自己完結型のスクリプトを作成する場合も-fオプションを用います。

script.awk

スクリプトを実行するために、chmodコマンドで実行権限を付与すると、スクリプトを実行できるようになります。

 

ファイルの最初の行にある#!(シバン,shebang)の後に、インタプリタ言語のプログラムを指定することで、その後の行はそのインタプリタでのプログラムとして解釈し、実行できます。
#!のプログラムの指定は絶対パスで指定し、#!のあるスクリプトを実行させた場合、シェルは、#!の行、スクリプトファイル名、実行するときのスクリプトファイルにある引数となるように実行されます。

つまり、上のscript.awkの実行コマンド

$ ./script.awk

$ /usr/bin/awk -f ./script.awk

のように解釈されて実行されます。

 

 

 

awkの変数と定数

awkの変数の初期値は空の文字列になります。また、変数を数値として扱う場合、その初期値は0になります。

script2.awk

コマンド例と実行結果

 

コマンドラインでも変数を代入することができます。一つは-vオプションを用いて変数を代入する方法があります。もう一つはコマンドライン引数に変数の代入を行う方法になります。

testdata.txt

コマンド例と実行結果(-vオプションを用いた場合)

コマンド例と実行結果(コマンドライン引数に変数を代入する場合)

 

 

 

組み込み変数

awkで利用する組み込み変数の一部紹介します。変数$nはほしい列を抜き出すのに便利な変数になります。また、変数NRは、行ごとの処理内容を配列に保持したり、終了時の処理時にその配列から値をすべて取り出すような場合に便利な変数です。

組み込み変数を覚えておくとawkをより活用できるようになります。

組み込み変数

変数 意味
$n (nは数字) $0は行全体を表します。
$1,$2,$3,…は空白文字を区切り文字とした列を表します。
区切り文字は変数FSや-Fオプションで変更可能
NR プログラム開始時からの処理した行数
NF 入力行の列数
FS 列の区切り文字
デフォルトは空白文字
RS 行の区切り文字
デフォルトは改行
OFS print文等の出力列の区切り文字
デフォルトはスペース
ORS 出力行の区切り文字
デフォルトは改行
OFMT print文の数値の出力形式
デフォルトは%.6g
CONVFMT 数値から文字列の変換の形式
デフォルトは%.6g
FILENAME 入力ファイルの名前
FNR 現在のファイルの現在の行数
ARGC コマンドラインの引数の数
ARGV コマンドラインの値の配列
添え字は0からARGC-1まで
ENVIRON 環境変数の連想配列
環境変数PATHの値がほしい場合は、ENVIRON["PATH"]のように指定

 

 

 

定数

awkの定数は主に数値、文字列、正規表現になります。

数値は以下のようなものになります。

文字列は以下のようなものになり、ダブルクォーテーションで文字列を囲みます。

正規表現は/.../で文字列を囲んだものになります。この正規表現の定数は'~'や'!~'の右辺として利用します。また、/.../の文字列を単独の場合、その意味は'$0 ~ /.../'のような意味になります。

 

 

 

文字列と数値の変換

awkは文字列と数値を文脈によって、変換します。例えば、

の結果は

のように表示されます。これはまず、数値1と数値2が文字列として変換・結合されます。その後、前の結果の文字列12は数値12に変換され、数値の3が加算されます。結果として、15と表示されます。これは変数でも同様です。

 

文字列から数値の変換について、文字列から数値の変換は文字列の最初の数字が数値として変換されます。例えば、

のようになります。また、数値として変換できないような文字列は0として扱われます。

 

 

 

awkの文

print文/printf文

print文やprintf文は、文字や数値を出力できます。

print文の構文は

になります。引数全体やアイテムの項目は、'()'のような括弧でくくることができます。括弧の有無によって、'>'の演算子をリダイレクトとして扱わずに、関係の演算子で扱うようにすることができます。

アイテムの引数は組み込み変数OFSで設定された区切り文字で、区切られて出力されます。また、組み込み変数OFSのデフォルトはスペースになります。

コマンド例と実行結果

 

printf文の構文は

であり、これはC言語のprintf関数のように、引数はフォーマット文字列とフォーマット指定子に入る値になります。

コマンド例と実行結果

 

 

 

リダイレクション(redirection)

print文やprintf文はシェルのように出力をリダイレクト(redirect)したり、パイプ(pipe)に送ることができます。

例えば、特定のファイルに出力する場合は

のようにファイルにリダイレクトすることができます。また、パイプを用いる場合は以下のように利用できます。

testdata.txt

コマンド例と実行結果

 

リダイレクトやパイプを利用すると、ファイルやパイプはオープンされた状態になります。これはawkが終了するまで、クローズされません。多くのファイルをオープンするとエラーが発生するため、多くのファイルをオープンする場合などは、必要な処理が終了した後にできるだけすぐにclose関数を呼び出し、ファイルやパイプをクローズします。

また、close関数で閉じるファイルやパイプの指定は、リダイレクトやパイプを使用した文字列を引数にします。なので、変数などにファイル名やコマンドを代入するとよいでしょう。

script3.awk

コマンド例と実行結果

 

 

 

条件分岐

if文は

のような構文になります。

if-statement.awk

コマンド例と実行結果

 

if文の内容の文が一つならば、

のように'{}'で囲む必要はありません。また、そのまま一文で記述するならば、上のスクリプトは以下のように記述し実行できます。
コマンド例と実行結果

 

switch文は

のような構文になります。switch文はC言語のswitch文のように利用できます。

switch-statement.awk

コマンド例と実行結果

 

 

 

ループ

for文は

のような構文になります。

コマンド例と実行結果

 

また、for文の別の形式として

のような構文もあります。これは配列の全要素に対して、アクセスするのに便利な構文になります。

コマンド例と実行結果

 

while文は

のような構文になります。

コマンド例と実行結果

 

do~while文は

のような構文になります。

コマンド例と実行結果

 

continue文はループ処理の残りの部分を飛ばし、即座に次のループに移行できます。
break文は、ループ処理の抜け出しやswitch文の抜け出しに利用できます。

 

 

 

next文/nextfile文

next文は呼び出した時点で、現在行の処理を飛ばして、次の行の処理に移行します。

コマンド例と実行結果

 

nextfile文も同様に、現在のファイルの処理を飛ばして、次のファイルの処理に移行します。

コマンド例と実行結果

 

 

 

exit文

exit文はawkの処理を終了し、ENDパターンの処理を行い、終了ステータスを返します。また、ENDパターン内でのexit文はそのままawkの処理が終了します。

コマンド例と実行結果

 

 

 

getline文

getline文は次の行を読み込みます。next文と異なり、処理は継続して行われます。また、getline文は引数に変数を取ることができます。変数が存在する場合はその変数に行の値が代入されます。引数が存在しない場合は$0の変数が変更されます。また、NR等の変数も書き換わります。

コマンド例と実行結果

 

getline文はリダイレクトを用いて、ファイルの行を読み込むことができます。

コマンド例と実行結果

 

他にも、パイプから行を読み込むこともできます。

コマンド例と実行結果

 

ファイルのオープンやパイプからの読み込みも一度使用したら、awkが終了するまでオープンされた状態になります。これはprint文のリダイレクトのようにclose関数でファイルやパイプをクローズできます。

 

 

 

awkのパターン

awkのルールは

になります。このパターンに当てはまる式は大まかに3つあります。それは正規表現と式と範囲があります。

正規表現(regular expression)

正規表現は文字列を/.../のように囲んだものになります。パターンがこの正規表現にマッチしている行のときにアクションが実行されます。

testdata.txt

コマンド例と実行結果

 

 

 

式(expression)

パターンの式の値が数値の0ではない、または、空文字列ではない場合にアクションが実行されます。

コマンド例と実行結果

 

 

 

範囲(range)

パターンをカンマで区切ることで始まりのパターンから終わりのパターンまでの範囲に対して、アクションを実行することができます。

コマンド例と実行結果

 

 

 

その他のパターン

その他のパターンとして以下の特別のパターンがあります。

特別なパターン

パターン 意味
BEGIN 開始時の処理
END 終了時の処理
BEGINFILE ファイルの読み込み開始時の処理
ENDFILE ファイルの読み込み終了時の処理
記述なし 全ての行に対する処理

 

 

 

awkの関数

関数の定義

関数は以下のように定義することができます。

script4.awk

 

awkで関数での変数のローカル宣言を行いたい場合は、関数の引数にその変数を追加することで、ローカル宣言のように扱うことができます。また、関数の引数かまたは関数のローカル変数かを区別するために、ローカル変数に余分な空白を追加するようなコーディング規則があります。

script5.awk

コマンド例と実行結果

 

関数はreturn文を利用することで値を返すこともできます。

script6.awk

コマンド例と実行結果

 

 

 

組み込み関数

組み込み関数については、The GNU Awk User’s Guide: Built-in Functionsで確認できます。

 

 

 

参考

Gawk: Effective AWK Programming