今野 英明
平成27年5月8日
この章では,改行記号で区切られたレコード(行)から, 特定の記号で区切られたフィールド(列)を抜き出す方法を学ぶ。
awk の名前はその開発者達の名前 Alfred V. Aho, Peter J. Weinberger そして Brian W. Kernighan から来ているものである。 awk のオリジナルバージョンは 1977 年に AT&T のベル研究所で開発された。
インターネット上の日本語訳としては,Edition 1.0.4版がある。 なお,この授業で利用する awk は GNU 版の gawk である。
この教材テキストには改行コードで区切られたがあり, その行の中に + (プラス) 記号で区切られた項目(列)が存在する。 awk では,各行をレコードと呼ぶ1。 さらにその中の,ある記号で区切られた項目(列)をフィールドと呼ぶ。
awk は,このような項目(列),すなわちフィールドを指定して, さまざまな操作をすることに適している言語である。
例えば,教材テキスト
単語表記+カタカナ読み+活用見だし語+品詞コード+ローマ字読み(改行コード)のうち,1 列目の単語表記フィールドのみを取り出して less で閲覧するには,
gawk -F+ '{print $1}' /pub/db_a/data/75.60k.vocab.romaji | less
を実行すればよい。
gawk [オプション] 'awkのプログラム命令' [対象とするファイル]
この授業では,gawk の各種オプションおよびプログラム命令のうち
gawk -F+ '{print $1}' /pub/db_a/data/75.60k.vocab.romaji | less
において,
$1 は区切り記号+で区切られた第 1 番目のフィールドを表している。
では,$1 を $2 や $3 等に変更するとどうなるか,
試してみよう。
さらに,$番号をカンマで区切って並べれば,フィールドの出力をいろいろと変更することができる。
gawk -F+ '{print $2, $1}' /pub/db_a/data/75.60k.vocab.romaji | less
次の例も試してみよう。
gawk -F+ '{print $0}' /pub/db_a/data/75.60k.vocab.romaji | less
実行結果からわかるとおり,$0 は処理中の行全体を格納する変数である。
gawk -F+ '{print $2 $1}' /pub/db_a/data/75.60k.vocab.romaji | less
とすれば,$2 と $1 の間は区切られない
($2 と $1 を結合して出力する)
2。
教材テキストでは,列(フィールド)が + で区切られているが, / を区切り文字とみなすように awk に指示してみよう。
gawk -F/ '{print $2}' /pub/db_a/data/ame | less
ここで,/pub/db_a/data/ame は,
教材テキストから ame を含む行のみを取り出したファイルである。
これと同じ中身のファイルは前回の練習問題でも ~/db15
に作成したので,
代わりにそちらを使ってもよい。
パイプ (|
) とは,
あるコマンドの出力を別のコマンドの入力に与えるものであり,
パイプを使えば,あるコマンドの処理結果を,
引き続き別のコマンドで処理することが可能になる。
例えば,
コマンドの出力を less で読む
コマンド | less
は,よく使われるパイプの一利用法である。
この章では, コマンドの標準入力とパイプについての要点を確認した上で, これまでに紹介した grep や awk 等をパイプと共に使うことにより, レコード(行)やフィールド(列)に対するより複雑な処理を行う方法を学ぶ。
なお,この章で紹介する標準入力やパイプについての詳細は,例えば, 「情報機器の操作」テキスト の「11. 標準入力の利用 -- リダイレクトとパイプ」に説明がある。
コマンド行にファイル名を指定せずに,
gawk '{print $1, $2}'
とだけ打って awk を実行してみよう。
新しいプロンプトがすぐに現れないのは,awk がまだ動いているためである。 続いて,キーボードから
abc def ghi
と打ってエンターを押してみよう。
abc def
と出力される。
さらに,
a bcd ef g
と打ってエンターを押すと,a bcd
と出力される。
これらの出力が得られたのは,
実行中の awk が '{print $1, $2}'
の命令に従って,
キーボードからの入力行を処理したためである。
オプション -F を指定していないので,
awk は空白を列の区切り文字とみなすことに注意しよう。
このように,ファイル名を指定しないで awk を実行すると, awk は標準入力 (通常はキーボード) からの入力を処理して出力する。 この awk を正常終了させるには, ctrl-d (EOF; 入力の終わり) を押せばよい。
gawk -F+ '{print $1}' /pub/db_a/data/75.60k.vocab.romaji | less
と同じことを,awk の引数にファイル名を与えずに行うには,
どうしたらよいだろうか。
前節では,ファイルの代わりにキーボードから abc def ghi 等の入力を与えたが,
75.60k.vocab.romaji の中身を全部キーボードから打ち込む訳にはいかない。
答えは, cat とパイプを使って,
cat /pub/db_a/data/75.60k.vocab.romaji | gawk -F+ '{print $1}' | less
とすればよい。
これを実行したときの動作は次のとおりである。
ここでは「| less
」については考えず,その左側だけに注目する。
|
は cat と awk の仲立ちをし,
cat の出力(教材テキストの中身)を awk の標準入力に直接流し込む。
awk 単体での実行時にファイル名を略すと標準入力はキーボードになるが,この実行例ではパイプによって awk の標準入力が cat の(標準)出力に結ばれた。
grep の実行例
grep asshukukuki /pub/db_a/data/75.60k.vocab.romaji
とパイプおよび gawk を組み合わせて,
教材テキストの中の asshukukuki を含む行 (レコード) の,
単語表記列 (フィールド) だけを出力する。
grep asshukukuki /pub/db_a/data/75.60k.vocab.romaji | gawk -F+ '{print $1}'
sort fileとすればよい。 file を省略すると sort も標準入力からデータを読み込む。 ここでは, 他のコマンドからの出力を sort コマンドに与え,辞書の逆順に並べ替えてみる。
さて,次の例が何をするのか,よく考えてから実行してみよう。 sort コマンドのオプション -r は逆順に並べ替えるものである。
grep ame /pub/db_a/data/75.60k.vocab.romaji | gawk -F+ '{print $5, $1}' | sort -r | less
``Effective AWK Programming'' には
awkの基本的な機能は、ファイルからあるパターンを含んでいる行(もしくは他のテキストの構成単位)を検索することである。 ある行がパターンの一つにマッチしたとき、awkは特定のアクションをその行に対して実行する。 awkはこのようにして入力ファイルの最後の行までそれぞれの行を処理し続ける。と記述されている。 ここにあるとおり, 指定したパターン (pattern) に一致する行に対してのみ, awk に動作 (action) を行わせるには, awk への「プログラム命令」を
pattern {action}の形式で与えればよい 3。 例えば,
gawk -F+ '/ame/ {print $1}' /pub/db_a/data/75.60k.vocab.romaji
は,入力行に ame を含む行の第1フィールドのみを出力する。
すなわち,ある文字列を含む行のみを awk で処理したければ,
pattern として「/文字列/」を指定すればよい。
これは
grep ame /pub/db_a/data/75.60k.vocab.romaji | gawk -F+ '{print $1}'
を実行するのと同じ結果を出力する。
また, pattern を「/文字列/」とする代わりに「/正規表現/」とすれば, 正規表現による行の指定も可能である。 正規表現については,前回のテキストを参照のこと。
次の例がどのような結果を出力するかを考えて,実行してみよう。
gawk -F+ '/ame/ {print $0}' /pub/db_a/data/75.60k.vocab.romaji
gawk -F+ '/n..kuni/ {print $0}' /pub/db_a/data/75.60k.vocab.romaji