AWK ( / ɔː k / [ 4 ] ) は、テキスト処理用に設計されたスクリプト言語で、通常はデータ抽出およびレポートツールとして使用されます。sed や grep と同様にフィルタであり[ 4 ] 、ほとんどのUnix 系オペレーティングシステムの標準機能です。AWKプロセッサを実行するシェルコマンドはです。awk
AWK 言語は、テキスト データストリームに対して実行される一連のアクションで構成されるデータ駆動型スクリプト言語です。ファイルに対して直接実行されるか、パイプラインの一部として使用され、フォーマットされたレポートの作成など、テキストの抽出や変換を目的としています。この言語は、文字列データ型、連想配列(つまり、キー文字列でインデックス付けされた配列)、および正規表現を多用します。AWK は想定されるアプリケーション ドメインが限られており、特に一行のプログラムをサポートするように設計されていましたが、チューリング完全であり、初期のベル研究所の AWK ユーザーでさえ、構造化された大規模な AWK プログラムを作成することがよくありました。[ 5 ]
AWKは1970年代にベル研究所で開発され[ 6 ]、その名前は開発者であるアルフレッド・アホ(egrepの開発者)、ピーター・ワインバーガー(小型リレーショナルデータベースの開発に携わった)、ブライアン・カーニハンの姓に由来しています。頭字語の発音は、鳥の種であるウミスズメ(auk)と同じで、 『The AWK Programming Language』の表紙に描かれています[ 7 ]。
Brian Kernighan によると、AWK の目標の 1 つは、数値と文字列の両方を簡単に操作できるツールを作ることだった。AWK はまた、入力データ内のパターンを検索するために使用されたMarc Rochkindのプログラミング言語に触発され、yaccを使用して実装された。[ 8 ]
バージョン 7 Unixに登場した初期のツールの 1 つです。AWK は、標準 Unix 環境で利用できる唯一のスクリプト言語であるBourne シェルに加えて、Unixパイプラインに計算機能を追加しました。これは、Single UNIX Specification [ 9 ]の必須ユーティリティの 1 つです。また、 Linux Standard Base仕様[ 10 ]で要求されています。
1983年当時、AWKはベル研究所のライセンスの下、チャールズ・リバー・データ・システムズのUNOSオペレーティングシステムで利用可能なUNIXツールの1つでした。[ 11 ]
AWK は 1985 ~ 1988 年にかけて大幅に改訂および拡張され、その結果、Paul Rubin、Jay Fenlason、およびRichard Stallmanによって書かれたGNU AWK実装が1988 年にリリースされました。[ 12 ] GNU AWK は GNU ベースの Linux パッケージに含まれているため、最も広く展開されているバージョンかもしれません[ 13 ] 。GNU AWK は1994 年以来、Arnold Robbinsによってのみメンテナンスされています。[ 12 ] Brian Kernighanのnawk (New AWK) ソースは、1993 年に初めて非公開でリリースされ、1990 年代後半から公開されています。多くの BSD システムでは、GPL ライセンスを回避するためにこれを使用しています。[ 12 ]
AWK の前身はsed (1974 年) でした。どちらもテキスト処理用に設計されています。行指向のデータ駆動型パラダイムを共有しており、暗黙のメインループと現在の行変数のおかげで、特に一行プログラムの記述に適しています。初期の AWK プログラムの強力さと簡潔さ、特に強力な正規表現処理と暗黙の変数による簡潔さ(一行プログラムを容易にする)は、当時の AWK の限界と相まって、Perl言語 (1987 年) の重要なインスピレーションとなりました。1990 年代には Perl が非常に人気を博し、Unix テキスト処理言語のニッチな分野で AWK と競合するようになりました。

AWKは入力データを1行ずつ読み込みます。プログラム内の各パターンごとに1行ずつスキャンし、一致するパターンごとに、それに対応するアクションを実行します。
—アルフレッド・V・アホ[ 14 ]
AWKプログラムは、パターンとアクションのペアの集合であり、次のように記述されます。
条件{アクション}条件{アクション} ...ここで、条件は通常式であり、アクションは一連のコマンドです。入力はレコードに分割され、デフォルトではレコードは改行文字で区切られているため、入力は行に分割されます。プログラムは各レコードを順番に各条件と照合し、真となる式ごとにアクションを実行します。条件またはアクションは省略できます。条件はデフォルトですべてのレコードに一致します。デフォルトのアクションはレコードを出力することです。これはsedと同じパターン・アクション構造です。
foo == 1や のような単純な AWK 式に加えて、/^foo/条件は、BEGINまたはENDにすることができ、これにより、すべてのレコードが読み込まれる前または後にアクションが実行されます。また、pattern1、pattern2は、 pattern1に一致するレコードから、pattern2に一致するレコードまでの範囲に一致し、その後、後続の行で再びpattern1との一致を試みます。
通常の算術演算子と論理演算子に加えて、AWK 式には~、正規表現を文字列と照合するチルダ演算子 が含まれています。便利な構文糖衣として、チルダ演算子を使用しない/regexp/ は現在のレコードと照合します。この構文はsedから派生しており、さらにedエディタから継承されています。ed エディタでは、検索に が使用されます。正規表現の区切り文字/としてスラッシュを使用するこの構文は、その後PerlとECMAScriptに採用され、現在では一般的です。チルダ演算子も Perl に採用されました。
AWKコマンドは、上記の例でアクションの代わりに用いられるステートメントです。AWKコマンドには、関数呼び出し、変数代入、計算、またはそれらの任意の組み合わせを含めることができます。AWKには多くの関数が標準でサポートされていますが、AWKの様々なバージョンではさらに多くの関数が提供されています。また、一部のバージョンでは動的リンクライブラリの組み込みをサポートしており、これによってさらに多くの関数を利用できるようになります。
printコマンドはテキストを出力するために使用されます。出力テキストは常に、出力レコード区切り文字 (ORS) と呼ばれる定義済みの文字列で終了します。ORS のデフォルト値は改行です。このコマンドの最もシンプルな形式は次のとおりです。
printprint $1print $1, $3これらのフィールド($X)は変数に似ているように見えるかもしれませんが($記号は通常のUnixシェルやPerlで変数を表します)、実際には現在のレコードのフィールドを参照しています。特別なケースとして、$0はレコード全体を参照します。実際、コマンド「print」と「print $0」は機能的に同じです。
printコマンドは、計算結果や関数呼び出しの結果を表示することもできます。
/regex_pattern/ { # レコード(行)が上記のregex_patternに一致する場合に実行するアクションprint 3 + 2 print foobar ( 3 ) print foobar ( variable ) print sin ( 3 - 2 ) }出力はファイルに保存できます。
/regex_pattern/ { # レコード(行)が上記のregex_patternに一致する場合に実行するアクションprint "expression" > "file name" }またはパイプを通して:
/regex_pattern/ { # レコード(行)が上記のregex_patternに一致する場合に実行するアクションprint "expression" | "command" }AWKの組み込み変数には、フィールド変数($1、$2、$3など、$0はレコード全体を表す)が含まれます。これらは、レコード内の個々のテキストフィールドのテキストまたは値を保持します。
その他の変数には以下が含まれます。
NR: レコード数。これまでにすべてのデータファイルから読み取られた入力レコードの現在のカウントを保持します。最初はゼロですが、自動的にゼロにリセットされることはありません。[ 15 ]FNR: ファイルレコード数。現在のファイルでこれまでに読み込まれた入力レコードの現在のカウントを保持します。この変数は、新しいファイルが開始されるたびに自動的にゼロにリセットされます。[ 15 ]NF: フィールド数。現在の入力レコードのフィールド数を示します。入力レコードの最後のフィールドは $NF、最後から 2 番目のフィールドは $(NF-1)、最後から 3 番目のフィールドは $(NF-2) などと指定できます。FILENAME現在の入力ファイル名が含まれます。FS: フィールド区切り文字。入力レコード内のフィールドを区切るために使用される「フィールド区切り文字」を指定します。デフォルトは「空白」で、任意のスペースとタブ文字のシーケンスを指定できます。FS には別の文字または文字シーケンスを再割り当てすることで、フィールド区切り文字を変更できます。RS: レコード区切り文字。現在の「レコード区切り文字」を格納します。デフォルトでは、入力行が入力レコードとなるため、デフォルトのレコード区切り文字は「改行」です。OFS: 出力フィールド区切り文字。awkが出力する際にフィールドを区切る「出力フィールド区切り文字」を格納します。デフォルトは「スペース」文字です。ORS: 出力レコード区切り文字。awkが出力レコードを出力する際に、レコードを区切る「出力レコード区切り文字」を格納します。デフォルトは「改行」文字です。OFMT: 出力形式。数値出力の形式を保存します。デフォルトの形式は「%.6g」です。言語キーワードを除き、変数名には数字で始まらない限り、以下の文字を使用できます 。
[ a - zA - Z_0 - 9 ] これらの演算子は+-*/^%、それぞれ加算、減算、乗算、除算、べき乗、剰余を表します。除算と剰余については、整数除算と浮動小数点除算の両方に同じ演算子が適用されます。整数オペランドから整数商を得るには、以下に示すように、被除数から剰余を事前に差し引く必要があります 。
awk 'BEGIN { OFMT = "%.14g"331 % - 79を印刷印刷- 3.31e-11 % 7.9e-23印刷331 / 79print ( 331 - 331 % 79 ) / 79} '15- 1.1999020635259e-234.18987341772154このint( dividend / divisor )アプローチでは、丸めが正しく行われないエッジケースが発生する可能性があります。文字列連結の場合は、2 つの変数 (または文字列定数) を隣り合わせに配置するだけです。文字列定数を使用する場合は間にスペースを入れるかどうかは任意ですが、隣り合わせに配置された 2 つの変数名には間にスペースが必要です。Unix シェルや Perl などの他の言語とは異なり、記号のない単一のアンダースコア変数名 ( _) は予約済みでも特別なものでもなく、ユーザーが自由に利用できます。デフォルト値は、数値ゼロと空文字列の両方を表す AWK の特殊な NULL 値であるため、Perl の を模倣して : の$_エイリアスとして使用することもできます$0 。
jot 30 | perl -nle ' print $_ << $_ ' | awk ' { print 2 ^ $_ * $ _ } '文字列定数は、二重引用符("...""")で囲まれ、二重引用符のみが囲まれます。文や式はセミコロンで終わる必要はありません。実際、セミコロンが;絶対に避けられない唯一の場所は、ループの 3 引数バリアントでfor ()、これは の対応するものとよく似ていますC/C++。関数へのすべての非配列引数は「値渡し」されるため、関数によって自由に変更可能になり、関数の上流呼び出し元に対して副作用がないことが保証されます。
Pythonとは異なり、awkなどの拡張代入はすべて+=-=*=/=^=%=式です。これにより、効果的な無制限の長さの連鎖が可能になります。その最大の利点の1つは、フィボナッチ数を計算するアルゴリズムによって実証されています。このアルゴリズムは、O(n) 自身に加算し続けるため、複雑さがになります。a += b += a += b += a += b += ... 最初の約77個のフィボナッチ数については、ビットを調べたり、変数を交換したり、再帰のオーバーヘッドを発生させたりする必要がないため、このアプローチは非常に効果的です。
#最後に、行の先頭文字として、またはコマンドやコマンドのシーケンスの後ろにを使用することで、プログラムにコメントを追加できます。
C言語と同様の形式で、関数定義はキーワードfunction、関数名、引数名、および関数本体で構成されます。以下に関数の例を示します。
function add_three ( number ) { return number + 3 }このステートメントは次のように呼び出すことができます。
(パターン) { print add_three ( 36 ) # 出力 '''39''' }関数にはローカルスコープの変数を含めることができます。これらの変数の名前は引数リストの末尾に追加されますが、関数呼び出し時にはこれらの変数の値は省略する必要があります。引数リストのローカル変数の前に空白を追加して、引数の終わりとローカル変数の始まりを示すのが慣例です。
以下は、 AWKで書かれたお決まりの「Hello, World!」プログラムです。
BEGIN { print "こんにちは、世界!" exit }80文字を超えるすべての行を印刷します。デフォルトでは、現在の行が印刷されます。
長さ($ 0 )> 80入力された単語数を数え、行数、単語数、文字数を表示します(wcのように)。
{ words += NF chars += length + 1 # 各レコード(行)の末尾にある改行文字を考慮して1を加算} END { print NR , words , chars }プログラムの最初の行にはパターンがないため、入力のすべての行がデフォルトで一致し、インクリメントアクションがすべての行に対して実行されます。words += NFは の省略形ですwords = words + NF。
{ s += $ NF } END { print s + 0 }s$NFは、AWK のフィールド区切り文字 (デフォルトでは空白) で定義される行の最後の単語である の数値によってインクリメントされます。NFは現在の行のフィールド数で、たとえば 4 です。$4は 4 番目のフィールドの値であるため、 は、$NFこの行のフィールド数や、周囲の行よりフィールド数が多いか少ないかに関係なく、行の最後のフィールドの値になります。は実際には、最も優先順位$の高い単項演算子です。 (行にフィールドがない場合、 は0 となり、は行全体になります。この場合、空白文字を除いて空であるため、数値は 0 になります。)NF$0
入力の最後にENDパターンが一致するため、sが出力されます。ただし、入力行がまったくない場合もあり、その場合はに値が割り当てられていないためs、sはデフォルトで空文字列になります。変数にゼロを加えることは、文字列から数値に強制変換するための AWK の慣用表現です。これは、加算などの AWK の算術演算子が、必要に応じて計算前にオペランドを暗黙的に数値にキャストするためです。(同様に、変数と空文字列を連結すると、数値から文字列に強制変換されますs ""。例: 。文字列を連結する演算子はなく、単に隣接して配置されることに注意してください。)入力が空の場合、の強制変換により{ print s + 0 }プログラムはを出力します0が、アクションだけの場合は{ print s }空行が出力されます。
NR % 4 == 1 , NR % 4 == 3 { printf "%6d %s\n" , NR , $ 0 }アクションステートメントは、番号を付けた各行を出力します。printf 関数は標準 C のprintf をエミュレートし、上記の print コマンドと同様に動作します。ただし、一致させるパターンは次のように動作します。NRは、 AWK がこれまでに読み込んだレコード数、通常は入力行数、つまり現在の行番号で、入力の最初の行は 1 から始まります。%は剰余演算子です。NR % 4 == 1は、入力の 1 行目、5 行目、9 行目などで真になります。同様に、NR % 4 == 3 は、入力の 3 行目、7 行目、11 行目などで真になります。範囲パターンは、最初の部分が 1 行目で一致するまで偽であり、その後、2 番目の部分が 3 行目で一致するまで真のままです。その後、最初の部分が 5 行目で再び一致するまで偽のままです。
したがって、このプログラムは1行目、2行目、3行目を出力し、4行目をスキップして、5行目、6行目、7行目、といった具合に出力します。各行について、行番号(6文字幅のフィールド)と行の内容を出力します。例えば、この入力に対して実行すると次のようになります。
ローマ フィレンツェ ミラノ ナポリ トリノ ヴェネツィア
前のプログラムの出力は以下のとおりです。
1 ローマ 2 フィレンツェ 3 ミラノ 5 トリノ 6 ベニス
特殊なケースとして、範囲パターンの最初の部分が常に真である場合(例:1)、範囲は入力の先頭から始まります。同様に、2番目の部分が常に偽である場合(例:0)、範囲は入力の末尾まで続きます。例:
/^--ここで切り取り--$/ 、0正規表現^--cut here--$に一致する最初の行、つまり「--cut here--」というフレーズのみを含む行から、入力行の末尾までを出力します。
BEGIN { FS = "[^a-zA-Z]+" } { for ( i = 1 ; i <= NF ; i ++ ) words [ tolower ( $ i )] ++ } END { for ( i in words ) print i , words [ i ] }BEGINブロックは、フィールド区切り文字を任意の非アルファベット文字のシーケンスに設定します。区切り文字は正規表現でも構いません。その後、各入力行に対してアクションを実行する基本的なアクションに進みます。この場合、行の各フィールドについて、小文字に変換された単語の出現回数に1を加算します。最後に、ENDブロックで、単語とその出現頻度を出力します。
(iを単語で表す)
は、配列words をループ処理し、配列の各添え字をiに設定するループを作成します。これは、このようなループが配列の各値を処理することとは異なります。したがって、ループは各単語とその出現頻度を出力します。これは、書籍の出版後に One True awk (下記参照) に追加された機能です。tolower
このプログラムはいくつかの方法で表現できます。最初の方法は、Bourneシェルを使用してすべての処理を行うシェルスクリプトを作成することです。これはこれらの方法の中で最も短いものです。
#!/bin/shpattern = " $1 " shift awk '/' " $pattern " '/ { print FILENAME ":" $0 }' " $@ "awk コマンドの$patternは単一引用符で保護されていないため、シェルは変数を展開しますが、スペースを含むパターンを適切に処理するには二重引用符で囲む必要があります。通常の方法では、パターン単独では行全体 ( $0) が一致するかどうかを確認します。FILENAMEには現在のファイル名が含まれます。 awk には明示的な連結演算子はありません。隣接する 2 つの文字列が連結されます。 は、$0変更されていない元の入力行に展開されます。
これには別の書き方があります。このシェルスクリプトは、awk 内から直接環境にアクセスします。
#!/bin/shexport pattern = " $1 " shift awk '$0 ~ ENVIRON["pattern"] { print FILENAME ":" $0 }' " $@ "ENVIRONこれは、本書の出版後に新しいバージョンの awk で導入された配列であるを使用するシェルスクリプトです。 の添え字はENVIRON環境変数の名前であり、その結果はその変数の値です。これは、さまざまな標準ライブラリやPOSIX のgetenv関数に似ています。このシェルスクリプトは、最初の引数を含む環境変数を作成し、その引数を削除してから、awk に各ファイルでパターンを探させます。pattern
~左オペランドが右オペランドと一致するかどうかをチェックします。!~はその逆です。正規表現は単なる文字列であり、変数に格納できます。
次の方法はコマンドライン変数代入を使用するもので、awkへの引数を変数への代入とみなすことができます。
#!/bin/shpattern = " $1 " shift awk '$0 ~ pattern { print FILENAME ":" $0 }' pattern = " $pattern " " $@ "または、コマンドラインオプション-v var=value を使用することもできます (例: awk -v pattern="$pattern" ... )。
最後に、これはシェルを使わず、awkスクリプトの実装についてあまり詳しく知る必要もなく(コマンドラインでの変数代入のように)、純粋なawkで書かれていますが、少し長くなります。
BEGIN { pattern = ARGV [ 1 ] for ( i = 1 ; i < ARGC ; i ++ ) # 最初の引数を削除ARGV [ i ] = ARGV [ i + 1 ] ARGC -- if ( ARGC == 1 ) { # パターンが唯一のものだったので、標準入力 (book で使用) から強制的に読み込むARGC = 2 ARGV [ 1 ] = "-" } } $ 0 ~ pattern { print FILENAME ":" $ 0 }これはBEGIN、最初の引数を抽出するだけでなく、BEGINブロックの終了後にファイル名として解釈されないようにするためにも必要です。ARGCは引数の数で、常に ≥ 1 であることが保証されています。 はARGV[0]スクリプトを実行したコマンドの名前で、多くの場合文字列です"awk"。ARGV[ARGC]は空文字列で、""は#行末まで展開されるコメントを開始します。
ブロックに注目してくださいif。awk はコマンドを実行する前に標準入力から読み込む必要があるかどうかだけを確認します。つまり、
awk 'prog'
ファイル名がないという事実は、prog実行前にのみチェックされるため、この方法は機能します。ARGC引数がないように明示的に 1 に設定すると、入力ファイルがなくなったと判断されるため、awk は単純に終了します。したがって、特別なファイル名を使用して標準入力から読み込むように明示的に指定する必要があります-。
Unix系オペレーティングシステムでは、シェバン構文を使用して自己完結型のAWKスクリプトを作成できます。
例えば、指定されたファイルの内容を標準出力に送信するスクリプトは、print.awk次のような内容のファイルを作成することで作成できます。
#!/usr/bin/awk -f { print $ 0 }以下のように呼び出すことができます。./print.awk <filename>
これは-f、続く引数がAWKプログラムを読み込むファイルであることをawkに指示するもので、sedで使用されるフラグと同じです。これらのプログラムはどちらも一行コマンドで実行されることが多いため、デフォルトでは別のファイルではなく、コマンドライン引数として指定されたプログラムを実行します。
AWKは元々1977年に開発され、Unixバージョン7に同梱されて配布されました。
1985年、開発者たちはこの言語の拡張に着手し、特にユーザー定義関数の追加に力を入れました。この言語は1988年に出版された書籍『The AWK Programming Language』で解説されており、その実装はUNIX System Vのリリースで利用可能になりました。互換性のない旧バージョンとの混同を避けるため、このバージョンは「new awk」または「nawk」と呼ばれることもありました。この実装は1996年にフリーソフトウェアライセンスでリリースされ、現在もBrian Kernighan氏によってメンテナンスされています(下記の外部リンクを参照)。
UNIX/32Vなどの古いバージョンの Unix には、AWK を C に変換するものが含まれていました。カーニハンは awk をC++awkccに変換するプログラムを作成しましたが、その状態は不明です。[ 16 ]
tolowerこのバージョンには、上記で説明したやなどの書籍にはない機能もあります。詳細は、ソース アーカイブの FIXES ファイルをENVIRON参照してください。このバージョンは、たとえばAndroid、FreeBSD、NetBSD、OpenBSD、macOS、illumosなどで使用されています。Brian Kernighan と Arnold Robbins は、 nawkのソース リポジトリ github.com/onetrueawk/awk の主な貢献者です。gawk マニュアルには、さらに多くの AWK 実装のリストがあります。[ 24 ]
[AWKは]データ駆動型言語と呼ばれることが多い。プログラムステートメントは、プログラムの手順のシーケンスではなく、照合および処理する入力データを記述する。
アクション言語はチューリング完全であり、ファイルの読み書きができます。