Awkは、予測可能なパターンを含むテキストをスキャンして処理するためのユビキタスなUnixコマンドです。ただし、機能を備えているため、プログラミング言語とも呼ばれます。
紛らわしいことに、複数のawkがあります。 (または、1つしか存在できないと思われる場合は、複数のクローンがあります。) awk 、Aho、Weinberger、およびKernighanによって作成された元のプログラム、そして nawk 、 mawk 、およびGNUバージョン、 gawk 。 GNUバージョンのawkは、いくつかの独自の機能を備えた、移植性の高いフリーソフトウェアバージョンのユーティリティであるため、この記事はGNUawkに関するものです。
正式な名前はgawkですが、GNU + Linuxシステムでは、awkにエイリアスされ、そのコマンドのデフォルトバージョンとして機能します。 GNU awkに同梱されていない他のシステムでは、GNU awkをインストールして、awkではなくgawkと呼ぶ必要があります。この記事では、awkとgawkという用語を同じ意味で使用しています。
コマンドとプログラミング言語の両方であるため、awkは、そうでなければ並べ替えに任せられる可能性のあるタスクのための強力なツールになります。 、カット 、 uniq 、およびその他の一般的なユーティリティ。幸いなことに、オープンソースには冗長性のための余地がたくさんあるので、awkを使用するかどうかという質問に直面した場合、答えはおそらく確かな「たぶん」です。
awkの柔軟性の美しさは、タスクにawkを使用することをすでに約束している場合は、途中で何が起こっても、おそらくawkにとどまることができるということです。これには、データが配信された順序以外の方法でデータを並べ替える永遠の必要性が含まれます。
awkの並べ替え方法を調べる前に、使用するサンプルデータセットを生成します。エッジケースや意図しない複雑さに気を取られないように、シンプルに保ちます。これは、この記事で使用するサンプルセットです:
Aptenodytes;forsteri;Miller,JF;1778;Emperor
Pygoscelis;papua;Wagler;1832;Gentoo
Eudyptula;minor;Bonaparte;1867;Little Blue
Spheniscus;demersus;Brisson;1760;African
Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
Torvaldis;linux;Ewing,L;1996;Tux
これは小さなデータセットですが、さまざまなデータ型を提供します:
- 属と種の名前。これらは互いに関連付けられていますが、別個のものと見なされます
- 名前。カンマの後に最初のイニシャルが付いている場合もあります
- 日付を表す整数
- 任意の用語
- セミコロンで区切られたすべてのフィールド
学歴に応じて、これを2D配列またはテーブル、あるいは単に行で区切られたデータのコレクションと見なすことができます。 awkはテキスト以上のものを期待していないので、それをどのように考えるかはあなた次第です。解析方法をawkに指示するのはあなた次第です。
テキストデータセットを特定の定義可能なフィールド(スプレッドシートの「セル」を考えてください)で並べ替えるだけの場合は、並べ替えコマンドを使用できます。
入力の形式に関係なく、重要なデータの部分に集中できるように、入力のパターンを見つける必要があります。この例では、データは行とフィールドの2つの要素で区切られています。新しい各行は、新しいレコードを表します 、スプレッドシートまたはデータベースダンプに表示される可能性が高いように。各行には、個別のフィールドがあります。 (スプレッドシートのセルと考えてください)セミコロン(;)で区切られています。
Awkは一度に1つのレコードを処理するため、awkに与える指示を構成している間は、1行だけに集中できます。 1つの行で何をしたいのかを確立し、次の行とさらにいくつかの行で(精神的にまたはawkで)テストします。最終的に、必要なデータ構造を提供するためにawkスクリプトが実行する必要があることについての適切な仮説が立てられます。
この場合、各フィールドがセミコロンで区切られていることが簡単にわかります。簡単にするために、各行の最初のフィールドでリストを並べ替えるとします。
並べ替える前に、各行の最初のフィールドだけにawkを集中させる必要があるため、これが最初のステップです。ターミナルでのawkコマンドの構文はawkです。 、関連するオプション、awkコマンド、処理するデータのファイルで終わります。
$ awk --field-separator=";" '{print $1;}' penguins.list
Aptenodytes
Pygoscelis
Eudyptula
Spheniscus
Megadyptes
Eudyptes
Torvaldis
フィールドセパレータはBashシェルにとって特別な意味を持つ文字であるため、セミコロンを引用符で囲むか、前に円記号を付ける必要があります。このコマンドは、特定のフィールドに集中できることを証明する場合にのみ役立ちます。別のフィールドの番号を使用して同じコマンドを試して、データの別の「列」の内容を表示できます。
$ awk --field-separator=";" '{print $3;}' penguins.list
Miller,JF
Wagler
Bonaparte
Brisson
Milne-Edwards
Viellot
Ewing,L
まだ何も分類されていませんが、これは良い基礎です。
Awkは単なるコマンドではありません。これは、インデックスと配列および関数を備えたプログラミング言語です。これは、並べ替えるフィールドのリストを取得し、そのリストをメモリに保存して処理し、結果のデータを出力できることを意味するため、重要です。このような複雑な一連のアクションの場合、テキストファイルでの作業が簡単になるため、 sorter.awkという名前の新しいファイルを作成します。 次のテキストを入力してください:
#!/usr/bin/awk -f
BEGIN {
FS=";";
}
これにより、ファイルは、ファイルに含まれる行を実行するawkスクリプトとして確立されます。
BEGIN ステートメントは、一度だけ発生する必要があるタスクのためにawkによって提供される特別なセットアップ関数です。組み込み変数FSの定義 、フィールドセパレータの略です これは、awkコマンドで-field-separatorを使用して設定した値と同じです。 、一度だけ発生する必要があるため、 BEGINに含まれています ステートメント。
awkの配列
$ を使用して、特定のフィールドの値を収集する方法をすでに知っています。 フィールド番号とともに表記しますが、この場合、端末に出力するのではなく、配列に格納する必要があります。これはawk配列で行われます。 awk配列について重要なことは、キーと値が含まれていることです。この記事に関する配列を想像してみてください。次のようになります:作成者: "seth"、title: "awkで並べ替える方法"、length:1200 。 作成者などの要素 およびタイトル および長さ はキーであり、次の内容が値です。
ソートのコンテキストでのこれの利点は、任意のフィールドをキーとして、任意のレコードを値として割り当ててから、組み込みのawk関数 asorti()を使用できることです。 (インデックスで並べ替え)キーで並べ替えます。今のところ、あなたがのみ 2番目のフィールドで並べ替えたい。
Awkステートメントではない 特別なキーワードの前にBEGIN またはEND 各レコードで発生するループです。これは、データのパターンをスキャンし、それに応じて処理するスクリプトの一部です。 awkがレコードに注意を向けるたびに、 {}のステートメント (前に BEGINが付いていない場合 またはEND )が実行されます。
キーと値を配列に追加するには、変数を作成します(このサンプルスクリプトでは、 ARRAYと呼びます。 、これはひどくオリジナルではありませんが、非常に明確です)配列を含み、括弧内のキーと等号( =)の値を割り当てます。 。
{ # dump each field into an array
ARRAY[$2] = $R;
}
このステートメントでは、2番目のフィールドの内容( $ 2 )がキーワードとして使用され、現在のレコード( $ R )が値として使用されます。
asorti()関数
配列に加えて、awkには、一般的なタスクの迅速で簡単なソリューションとして使用できるいくつかの基本的な機能があります。 GNUawkで導入された関数の1つであるasorti() 、キー(またはインデックス)で配列を並べ替える機能を提供します )または値。
配列は、データが入力された後でのみ並べ替えることができます。つまり、このアクションは、すべての新しいレコードで発生する必要はなく、スクリプトの最終段階でのみ発生する必要があります。この目的のために、awkは特別な ENDを提供します キーワード。 BEGINの逆 、 END ステートメントは一度だけ発生し、すべてのレコードがスキャンされた後にのみ発生します。
これをスクリプトに追加します:
END {
asorti(ARRAY,SARRAY);
# get length
j = length(SARRAY);
for (i = 1; i <= j; i++) {
printf("%s %s\n", SARRAY[i],ARRAY[SARRAY[i]])
}
}
asorti() 関数はARRAYの内容を取得します 、インデックスで並べ替え、結果を SARRAYという新しい配列に配置します (この記事のために私が発明した任意の名前、つまりソートされた配列 。
次に、変数 j (別の任意の名前)には、 length()の結果が割り当てられます SARRAYのアイテム数をカウントする関数 。
最後に、 forを使用します SARRAYの各アイテムを反復処理するためのループ printf()を使用する 各キーを出力し、その後に ARRAYでそのキーの対応する値を出力する関数 。
awkスクリプトを実行するには、スクリプトを実行可能にします。
$ chmod +x sorter.awk
次に、 penguin.listに対して実行します サンプルデータ:
$ ./sorter.awk penguins.list
antipodes Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
chrysocome Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
demersus Spheniscus;demersus;Brisson;1760;African
forsteri Aptenodytes;forsteri;Miller,JF;1778;Emperor
linux Torvaldis;linux;Ewing,L;1996;Tux
minor Eudyptula;minor;Bonaparte;1867;Little Blue
papua Pygoscelis;papua;Wagler;1832;Gentoo
ご覧のとおり、データは2番目のフィールドで並べ替えられています。
これは少し制限があります。このスクリプトを任意のデータセットで使用して意味のある結果を得ることができるように、実行時にソートキーとして使用するフィールドを柔軟に選択できるようにすることをお勧めします。
リテラル値varを使用して、コマンド変数をawkスクリプトに追加できます。 スクリプトで。反復句がvarを使用するようにスクリプトを変更します アレイを作成するとき:
{ # dump each field into an array
ARRAY[$var] = $R;
}
-v var を使用して、3番目のフィールドでソートされるようにスクリプトを実行してみてください 実行時のオプション:
$ ./sorter.awk -v var=3 penguins.list
Bonaparte Eudyptula;minor;Bonaparte;1867;Little Blue
Brisson Spheniscus;demersus;Brisson;1760;African
Ewing,L Torvaldis;linux;Ewing,L;1996;Tux
Miller,JF Aptenodytes;forsteri;Miller,JF;1778;Emperor
Milne-Edwards Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
Viellot Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
Wagler Pygoscelis;papua;Wagler;1832;Gentoo
この記事では、純粋なGNUawkでデータを並べ替える方法を説明しました。スクリプトは改善できるので、役立つ場合は、gawkのマニュアルページでawk関数を調べて、出力を改善するためにスクリプトをカスタマイズしてください。
これまでの完全なスクリプトは次のとおりです。
#!/usr/bin/awk -f
# GPLv3 appears here
# usage: ./sorter.awk -v var=NUM FILE
BEGIN { FS=";"; }
{ # dump each field into an array
ARRAY[$var] = $R;
}
END {
asorti(ARRAY,SARRAY);
# get length
j = length(SARRAY);
for (i = 1; i <= j; i++) {
printf("%s %s\n", SARRAY[i],ARRAY[SARRAY[i]])
}
}