Skip to content

Latest commit

 

History

History
177 lines (115 loc) · 6.26 KB

007-awk.md

File metadata and controls

177 lines (115 loc) · 6.26 KB

awk

awkは、パターンスキャンと処理を行う言語である。

awkとはなんだろうか。現代を生きる読者にとってawkとは、自分のやりたいことを実現するコマンドを検索したところでてきたコマンド例になぜか付着しているよくわからない謎の呪文でしかないはずだ。この機会にawkの本質的な役割を学んでいこう

awkは、awkというプログラミング言語を使い、テキストに対するパターンマッチを行い、マッチに対して処理を行える言語だ。

UNIXのお作法では、プログラム間のデータの受け渡しはテキストだ。

問題:オカンはお使いの買い物リストのデータをテキストで標準出力に出力し、たかし君は標準入力からデータを受け取ります。このとき、シェルのパイプライン機能を使ってオカンからたかし君にデータを受け渡しなさい。

この問題は、

$ オカン | たかし君

と書ける。

もう少し具体的な問題を考えよう。

問題:オカンが出力するお使いの買い物リストは、各行に品物の値段が書かれています。たかし君がお使いに行くためにはいくら持っていけばいいでしょうか。

値段1
値段2
...

たかし君をawkで実装することもできるのだが、今回はシェルスクリプトで実装しておこう。

$ cat たかし君
#! /bin/sh

while read x;
do
    sum=$((sum+x))
done
echo $sum

問題:オカンは買い物リストに買うべき品物が書かれていないことに気がついたので、買い物リストのフォーマットを以下のように変更しました。

品物1   値段1
品物2   値段2
...

たかし君は一度書いたコードを変更したくありません。オカンもたかし君のためにわざわざ値段だけのデータを出力したくありません。どうすればいいでしょうか。

このような問題はUNIXではよくあることだ。すでに2つのとても有名なプログラムがあり、その出力と入力が決まっている場合、データのフォーマットのすり合わせを行わなければならない。こういうとき、awkが役に立つ。

プログラミング言語awkの基本は各行に対するパターンマッチと処理だ。

パターン { 処理 }

パターンは部分一致でいい。パターンがない場合はあらゆる入力にマッチする。

{ 処理 }

処理がない場合は単にその行が出力される。

パターン

これは

パターン { print }

と同じだ。

awk用語では行をレコードと呼び、行の空白文字で区切られた部分文字列をフィールドと呼ぶ。$1が1番目のフィールド、$2が2番目のフィールド、$0はレコード全体を意味する。

つまり、各行の値段だけのテキストに変換するには、各行にマッチするパターンで2番目のフィールドを出力すればいいということだ。すべての行にマッチするので、パターンは省略できる。

$ echo "1 2
>3 4
>5 6" | awk '{print $2}'
2
4
6

したがって、問題は以下のように書ける。

$ オカン | awk '{print $2}' | たかし君

問題:オカンは買い物リストにコメント機能がほしいと考えたため、#から始まる行をコメントとすることにしました。

#品物       値段
卵          200
鶏もも肉    500
# 今日は親子丼よ
鉛筆        200
消しゴム    300
# たかしの勉強道具よ

たかし君はまだプログラムを変更したくありません。

ここでawkのパターンを使う。行の先頭が#ではない行だけ処理したい。

まず行の先頭が#の場合を考える。awkのパターンは正規表現が使えるので、/^#/と書ける。awkはある式が正規表現パターンにマッチしない場合として、

exp !~ /regexp/

が使える。行は$0なので、パターン全体としては、

$0 !~ /^#/

となる。つまり、

$ おかん | awk '$0 !~ /^#/{print $2}' | たかし君

となる。

読者がやりたいことを実現するコマンドを検索すると呪文のようなawkに出くわすのは、このようにフォーマットのすり合わせをawkワンライナーで実現しているためだ。

問題:たかし君はいい加減に面倒になったのでawkで全部実装したくなりました。

プログラミング言語awkの文法はC言語に似ている。UNIXにおけるプログラミングはC言語なので、Cはユーザーに馴染み深い言語だったのだ。

変数や整数演算は極めてC言語に似ている。

{ sum += $2 }

あとは変数をどのように出力するかということだ。

awkのパターンにはBEGINENDがある。これは具体的なレコードではなく、BEGINはプログラムが開始するタイミング、ENDはプログラムが終了するタイミングにマッチする。

END { print sum }

これらを組み合わせると、awkでプログラムたかし君を実装できる。

$ cat たかし君
#!/bin/awk -f

$0 !~ /^#/ { sum += $2 }
END { print sum }

awkを覚えるとちょっとしたテキスト整形処理をワンライナーで書くことができる。awkの利点はPOSIX互換OSには絶対に同梱されていることにある。PerlやPythonがなくてもawkはある。awkのプログラミング言語としての表現力はなかなかのもので、POSIXコマンドの多くはawkで実装可能なのだ。

読者もこれを気に、今まで謎の呪文だと考えて脳死でコピペしていたawkを学んでみるとよい。

ちなみに、気になるawkの名前の由来だが、Bell Labsで1970年代にawkを実装した作者であるAlfred Aho, Peter Weinberger, Brian Kernighanに由来する。Alfred Ahoはドラゴンブックとして有名なコンパイラー本の作者で、Brian KernighanはK&R CのKのほうだ。Peter Weinbergerは世間的にはawkで一番良く知られている。

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html