ウェブサイト検索

AWK チュートリアル: Linux での AWK コマンドの 25 の実践例


Linux で AWK コマンドを使用する方法を知りたいですか?ここでは、AWK の基本をマスターするのに役立つ 25 の AWK コマンド例と適切な説明を示します。

AWK コマンドの歴史は、Unix の初期にまで遡ります。これは POSIX 標準の一部であり、Unix 系のシステムで利用できるはずです。以降。

AWK は、Perl のような多目的言語と比較して古かったり、機能が不足していたりするため、時々信用されないことがありますが、私が毎日の仕事で使いたいツールであることに変わりはありません。比較的複雑なプログラムを作成する場合もありますが、データ ファイルの問題を解決するために作成できる強力なワンライナーを使用する場合もあります。

したがって、これがまさにこの記事の目的です。 AWK の機能を活用して有用なタスクを実行する方法を 80 文字未満で示します。この記事は、完全な AWK チュートリアルを目的としたものではありませんが、最初にいくつかの基本的なコマンドを含めているので、これまでの経験がほとんどない場合でも、AWK の核となる概念を理解できるようになります。

この AWK チュートリアルのサンプル ファイル

この記事で説明されているすべてのワンライナーは、同じデータ ファイルでテストされます。

cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

そのファイルのコピーは、GitHub からオンラインで入手できます。

AWK の事前定義変数と自動変数について理解する

AWK は、プログラムの作成に役立つ、事前定義された自動変数をいくつかサポートしています。その中には、次のようなものによく遭遇します。

RSレコード区切り文字。 AWK は一度に 1 レコードずつデータを処理します。レコード区切り文字は、入力データ ストリームをレコードに分割するために使用される区切り文字です。デフォルトでは、これは改行文字です。したがって、変更しない場合、レコードは入力ファイルの 1 行になります。

NR現在の入力レコード番号。 レコードに標準の改行区切り文字を使用している場合、これは現在の入力行番号と一致します。

FS/OFSフィールド区切り文字として使用される文字。 AWK がレコードを読み取ると、次の条件に基づいてレコードをさまざまなフィールドに分割します。 FS の値。 AWK が出力にレコードを印刷すると、フィールドが再結合されますが、今回は FS 区切り文字の代わりに OFS 区切り文字が使用されます。通常、FSOFS は同じですが、これは必須ではありません。どちらも「空白」がデフォルト値です。

NF – 現在のレコード内のフィールドの数。フィールドに標準の「空白」区切り文字を使用している場合、これは現在のレコード内の単語数と一致します。

他にも多かれ少なかれ標準的な AWK 変数が利用できるため、詳細については特定の AWK 実装マニュアルを確認する価値があります。ただし、このサブセットは、興味深いワンライナーを書き始めるのにすでに十分です。

A. AWKコマンドの基本的な使い方

1. すべての行を印刷します

この例はほとんど役に立ちませんが、それでも AWK 構文の良い入門にはなります。

awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

AWK プログラムは、1 つまたは複数の pattern { action } ステートメントで構成されます。

入力ファイルの特定のレコード (「行 」) について、パターン が非-zero 値 (AWK の「true」に相当) を指定すると、 対応するアクション ブロック内のコマンドが実行されます。上記の例では、1 はゼロ以外の定数であるため、{ print } アクション ブロックが入力レコードごとに実行されます。

もう 1 つのトリックは、 { print } が、明示的に指定しない場合に AWK によって使用されるデフォルトのアクション ブロックであることです。したがって、上記のコマンドは次のように短縮できます。

awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

ほとんど役に立たないものの、次の AWK プログラムは入力を消費しますが、出力には何も生成しません。

awk 0 ファイル

2. ファイルヘッダーを削除します

awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

これは、明示的に次のように記述するのと同じであることに注意してください。

awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

このワンライナーは、最初のレコードを除いて入力ファイルのレコードを書き込みます。その場合、条件は 1>1 であり、明らかに true ではありません。

このプログラムは RS のデフォルト値を使用しているため、実際には入力ファイルの最初の行が破棄されます。

3. 範囲内の行を印刷します

これは前述の例を一般化したものにすぎず、 && が論理 and 演算子であることを除いて、多くの説明は必要ありません。

awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team

4. 空白のみの行を削除する

awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support
17,05 apr 2019,abhishek,guest

AWK は、FS 変数で指定されたフィールド区切り文字に基づいて、各レコードをフィールドに分割します。デフォルトのフィールド区切り文字は1 つまたは複数の空白文字 (スペースまたはタブとも呼ばれます) です。これらの設定を使用すると、空白以外の文字を少なくとも 1 つ含むレコードには、少なくとも 1 つのフィールドが含まれることになります。

つまり、 NF が 0 (「false」) になる唯一のケースは、レコードにスペースのみが含まれている場合です。したがって、このワンライナーでは、少なくとも 1 つの非スペース文字を含むレコードのみが出力されます。

5. すべての空白行を削除する

awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support

17,05 apr 2019,abhishek,guest

このワンライナーは、RS が空の文字列に設定されている場合、「その場合、レコードは プラスで構成されるシーケンスで区切られる」というあいまいな POSIX ルールに基づいています。 1 つ以上の空白行。 」

POSIX 用語で言及しておくと、空行は完全に空の行です。空白のみを含む行は「空白」としてカウントされません。

6. フィールドの抽出

これはおそらく AWK の最も一般的な使用例の 1 つであり、データ ファイルのいくつかの列を抽出します。

awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
,
,
17,abhishek

ここでは、入力フィールドと出力フィールドの区切り文字の両方を明示的にカンマに設定しています。 AWK がレコードをフィールドに分割すると、最初のフィールドの内容が $1 に格納され、2 番目のフィールドの内容が $2 に格納されます。ここではそれを使用しませんが、$0 がレコード全体であることに言及する価値があります。

このワンライナーでは、パターンのないアクション ブロックを使用していることに気づいたかもしれません。この場合、パターンとして 1 (「true」) が想定されるため、アクション ブロックはレコードごとに実行されます。

ニーズによっては、空白行または空白のみの行では期待どおりの結果が得られない場合があります。その場合、2 番目のバージョンはもう少し改善される可能性があります。

awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

どちらの場合も、コマンド ラインで FSOFS のカスタム値を渡しました。もう 1 つのオプションは、AWK プログラム内の特別な BEGIN ブロックを使用して、最初のレコードが読み取られる前にこれらの変数を初期化することです。したがって、あなたの好みに応じて、代わりに次のように書くことを好むかもしれません。

awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

ここで言及しておきたいのは、最後のレコードが読み取られた後に END ブロックを使用していくつかのタスクを実行することもできるということです。まさに今それを見ることになります。そうは言っても、空白のみの行はエレガントに処理されないため、これが完璧とは程遠いことは認めます。考えられる解決策はすぐにわかりますが、その前にいくつか計算してみましょう。

7. 列ごとの計算の実行

AWK は標準の算術演算子をサポートします。また、コンテキストに応じて値をテキストと数値の間で自動的に変換します。また、独自の変数を使用して中間値を保存することもできます。これにより、データ列に対して計算を実行するコンパクトなプログラムを作成できるようになります。

awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263

または、同等の += 短縮構文を使用します。

awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263

AWK 変数は使用前に宣言する必要がないことに注意してください。未定義の変数は空の文字列を保持すると想定されます。 AWK の型変換規則によれば、これは数値 0 と等しくなります。この機能のおかげで、$1 にテキスト (見出し)、空白、または何も含まれていない場合を明示的に処理する必要はありませんでした。これらすべての場合において、これは 0 としてカウントされ、合計には影響しません。もちろん、代わりに乗算を実行すると異なります。では、コメントセクションを使用してその場合の解決策を提案してみてはいかがでしょうか?

8. 空行以外の行数を数える

END ルールについては以前に説明しました。ファイル内の空ではない行の数を数える別の応用例を次に示します。

awk '/./ { COUNT+=1 } END { print COUNT }' file
9

ここでは COUNT 変数を使用し、正規表現 /./ に一致する行ごとに変数をインクリメント (+=1) しました。つまり、各行には少なくとも 1 文字が含まれます。最後に、END ブロックは、ファイル全体が処理された後の最終結果を表示するために使用されます。 COUNT という名前には特別なことは何もありません。 Countcountnxxxx、または AWK 変数の命名規則に準拠したその他の名前を使用することもできました。

しかし、この結果は正しいのでしょうか?まあ、それは「空」行の定義によって異なります。 (POSIX に従って) 空白行のみが空であると考える場合、これは正しいです。しかし、空白のみの行も空であると考えたほうがよいでしょうか?

awk 'NF { COUNT+=1 } END { print COUNT }' file
8

今回の結果は異なります。初期バージョンでは空白行のみが無視されたのに対し、新しいバージョンでは空白のみの行も無視されるためです。違いが分かりますか?それは自分で考えさせます。十分に明確でない場合は、ためらわずにコメントセクションを使用してください。

最後に、データ行のみに興味があり、特定の入力データ ファイルが与えられている場合は、代わりに次のように書くことができます。

awk '+$1 { COUNT+=1 } END { print COUNT }' file
7

これは、AWK 型変換ルールにより機能します。パターン内の単項プラスは、数値コンテキストで $1 の評価を強制します。私のファイルでは、データ レコードの最初のフィールドに数値が含まれています。非データ レコード (見出し、空行、空白のみの行) にはテキストが含まれるか、何も含まれません。数値に変換すると、それらはすべて 0 に等しくなります。

最新のソリューションでは、最終的にクレジットが 0 になったユーザーのレコードも破棄されることに注意してください。

B. AWK での配列の使用

配列は AWK の強力な機能です。 AWK の配列はすべて連想配列であるため、任意の文字列を別の値に関連付けることができます。他のプログラミング言語に精通している場合は、ハッシュ連想テーブルなどとして知っているかもしれません。 辞書または地図

9. AWK配列の簡単な例

すべてのユーザーの合計クレジットを知りたいと想像してみましょう。各ユーザーのエントリを連想配列に保存し、そのユーザーのレコードを見つけるたびに、配列に保存されている対応する値をインクリメントします。

awk '+$1 { CREDITS[$3]+=$1 }
     END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109

これはもはやワンライナーではないと認めます。主な原因は、ファイルの処理後に配列の内容を表示するために使用される for ループです。それでは、短い例に戻りましょう。

10. AWK を使用した重複行の特定

配列は、他の AWK 変数と同様に、アクション ブロックとパターンの両方で使用できます。これを利用して、重複行のみを出力するワンライナーを作成できます。

awk 'a[$0]++' file
52,01    dec   2018,sonia,team

++ 演算子は、C 言語ファミリーから継承された後置インクリメント演算子です (AWK は、そのオリジナル作成者の 1 人である Brian Kernighan のおかげで、その誇り高いメンバーです)。

その名前が示すように、後置インクリメント演算子は変数をインクリメント (「add 1 」) しますが、それは変数の値がエングロービング式の評価に取得された後でのみです。

その場合、a[$0] が評価されてレコードが印刷されるかどうかが確認され、決定が下されると、すべての場合において配列エントリがインクリメントされます。

したがって、レコードが初めて読み取られるとき、a[$0] は未定義であるため、AWK のゼロと同等です。そのため、最初のレコードは出力に書き込まれません。次に、そのエントリは 0 から 1 に変更されます。
同じ入力レコードが 2 回目に読み取られると、a[$0] は 1 になります。これは「true」です。行が印刷されます。ただし、その前に、配列エントリが 1 から 2 に更新されます。以下同様です。

11. 重複行の削除

前のワンライナーの必然として、重複した行を削除する必要があるかもしれません。

awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support


17,05 apr 2019,abhishek,guest

唯一の違いは、式の真理値を反転する論理演算子 (!) を使用していることです。偽りだったものが真実になり、真実だったものが偽りになる。論理否定は、以前とまったく同じように動作する ++ ポストインクリメントにはまったく影響しません。

C. フィールドとレコードの区切りマジック

12. フィールド区切り文字の変更

awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

このプログラムは、入力フィールドの区切り文字としてカンマを使用し、出力フィールドの区切り文字としてセミコロンを使用するように、FS 変数と OFS 変数を設定します。フィールドを変更しない限り、AWK は出力レコードを変更しないため、$1=$1 トリックを使用して、AWK にレコードを強制的に分割し、出力フィールド区切り文字を使用して再構築させます。

ここで、デフォルトのアクション ブロックが { print } であることに注意してください。したがって、これをより明示的に次のように書き直すことができます。

awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

これらの例では両方とも空行も削除されていることに気づいたかもしれません。なぜ?さて、AWK 変換ルールを思い出してください。空の文字列は「false」です。他のすべての文字列は「true」です。式 $1=$1 は、$1 を変更する影響です。ただし、これも表現です。そして、 $1 の値として評価されます。これは、空の文字列の場合は「false」です。本当にすべての行が必要な場合は、代わりに次のような記述が必要になる場合があります。

awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

&& 演算子を覚えていますか?それは論理積でした。 || は論理 OR です。演算子の優先順位規則のため、ここでは括弧が必要です。これらがなければ、パターンは代わりに $1=($1 || 1) として誤って解釈されていたでしょう。練習として、そのとき結果がどのように変わったかをテストしてみましょう。

最後に、算術にあまり興味がない場合は、きっと次のような単純な解決策を好むでしょう。

awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

13. 複数のスペースの削除

awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest

これは前のプログラムとほぼ同じです。ただし、フィールド区切り文字はデフォルト値のままにしました。したがって、複数の空白が入力フィールドの区切り文字として使用されますが、出力フィールドの区切り文字として使用されるのは 1 つのスペースだけです。これには複数の空白を1 つの空白に結合するという素晴らしい副作用があります。

14. AWKを使用した線の結合

出力フィールド区切り文字である OFS をすでに使用しています。ご想像のとおり、出力レコード区切り文字を指定するための ORS が対応しています。

awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle    abhishek

ここでは、各レコードの後に改行文字の代わりにスペースを使用しました。一部のユースケースではこのワンライナーで十分ですが、それでもいくつかの欠点があります。

最も明らかなのは、空白のみの行は破棄されないことです (öle の後の余分なスペースはそこから来ています)。したがって、代わりに単純な正規表現を使用することになるかもしれません。

awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek

現在は改善されていますが、まだ問題が発生する可能性があります。区切り文字を目に見えるものに変更すると、より明確になります。

awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+

フィールド区切り文字は各レコードのに記述されるため、行の最後に余分な区切り文字があります。最後のものも含めて。

これを修正するには、2 番目の出力レコードから開始して、レコードのにカスタム区切り文字を表示するようにプログラムを書き直します。

awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek

区切り文字の追加は自分で行うため、標準の AWK 出力レコード区切り文字も空の文字列に設定します。ただし、区切り文字や書式設定を扱い始める場合は、print ステートメントの代わりに printf 関数の使用を検討する必要があるかもしれません。今からわかるように。

D. フィールドのフォーマット

AWK と C プログラミング言語の関係についてはすでに述べました。とりわけ、AWK は C 言語標準ライブラリから強力な printf 関数を継承しており、出力に送信されるテキストの書式設定を大幅に制御できます。

printf 関数は、最初の引数としてフォーマットを受け取ります。このフォーマットには、そのまま出力されるプレーン テキストと、出力のさまざまなセクションのフォーマットに使用されるワイルドカードの両方が含まれます。ワイルドカードは、% 文字によって識別されます。最も一般的なものは、%s (文字列の書式設定用)、%d (整数の書式設定の場合)、および %f (浮動小数点数の書式設定の場合) です。 )。これはかなり抽象的になる可能性があるため、例を見てみましょう。

awk '+$1 { printf("%s ",  $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek

print ステートメントとは逆に、printf 関数は OFSORS を使用しないことに気づくかもしれません。価値観。したがって、何らかの区切り文字が必要な場合は、私が行ったように、フォーマット文字列の末尾にスペース文字を追加して、区切り文字を明示的に指定する必要があります。これは、出力を完全に制御するために支払う代償です。

まったく形式指定子ではありませんが、改行文字を表すために任意の AWK 文字列で使用できる \n 表記を導入する良い機会です。

awk '+$1 { printf("%s\n",  $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek

15. 表形式の結果の生成

AWK は、区切り文字に基づいてレコード/フィールドのデータ形式を強制します。ただし、printf 関数を使用すると、固定幅の表形式の出力を生成することもできます。 printf ステートメント内の各形式指定子はオプションの幅パラメータを受け入れることができるため、次のようになります。

awk '+$1 { printf("%10s | %4d\n",  $3, $1) }' FS=, file
   sylvain |   99
     sonia |   52
     sonia |   52
     sonia |   25
   sylvain |   10
       öle |    8
  abhishek |   17

ご覧のとおり、各フィールドの幅を指定すると、AWK はフィールドの左側にスペースを埋め込みます。テキストの場合は、通常、右側をパディングすることが推奨されます。これは、負の幅の数値を使用して実現できます。また、整数の場合は、フィールドにスペースの代わりにゼロを埋め込みたい場合があります。これは、フィールド幅の前に明示的に 0 を使用することで取得できます。

awk '+$1 { printf("%-10s | %04d\n",  $3, $1) }' FS=, file
sylvain    | 0099
sonia      | 0052
sonia      | 0052
sonia      | 0025
sylvain    | 0010
öle        | 0008
abhishek   | 0017

16. 浮動小数点数の扱い

%f 形式については、あまり説明する必要はありません…

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429

…ただし、ほとんどの場合、表示される結果のフィールド幅と精度を明示的に設定する必要があると言えるでしょう。

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG=  37.6

ここで、フィールド幅は 6 です。これは、フィールドが 6 文字のスペースを占めることを意味します (ドットを含み、最終的には通常のように左側にスペースが埋め込まれます)。 .1 精度は、ドットの後に 10 進数を 1 つ付けて数値を表示することを意味します。代わりに %06.1 が何を表示するかはご想像にお任せします。

E. AWK での文字列関数の使用

printf 関数に加えて、AWK には他にも優れた文字列操作関数がいくつか含まれています。この分野では、Gawk のような最新の実装は、移植性の低下を犠牲にして、より豊富な内部関数セットを備えています。私自身としては、ここではどこでも同じように動作する POSIX 定義関数をいくつか取り上げることにします。

17. テキストを大文字に変換する

これは、国際化の問題を適切に処理するため、私はよく使用します。

awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01    DEC   2018,SONIA,TEAM
52,01    DEC   2018,SONIA,TEAM
25,01    JAN   2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12    JUN   2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST

実際のところ、これはおそらく、シェルからテキストを大文字に変換するための最良かつ最も移植可能なソリューションです。

18. 文字列の一部を変更する

substr コマンドを使用すると、文字列を指定した長さで分割できます。ここでは、3 番目のフィールドの最初の文字のみを大文字にするために使用します。

awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01    dec   2018,Sonia,team
52,01    dec   2018,Sonia,team
25,01    jan   2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12    jun   2018,Öle,team:support
17,05 apr 2019,Abhishek,guest

substr 関数は、初期文字列、抽出する最初の文字の (1 から始まる) インデックス、および抽出する文字数を受け取ります。最後の引数が欠落している場合、substr は文字列の残りの文字をすべて受け取ります。

したがって、substr($3,1,1)$3 の最初の文字として評価され、substr($3,2) は残りの文字として評価されます。もの。

19. フィールドをサブフィールドに分割する

AWK レコード フィールド データ モデルは非常に優れています。ただし、内部区切り文字に基づいてフィールド自体をいくつかの部分に分割したい場合があります。

awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019

少し驚いたことに、これはフィールドの一部が複数の空白で区切られている場合でも機能します。主に歴史的な理由により、区切り文字が 1 つのスペースである場合、split は「要素は一連の空白によって区切られている」とみなします。 」 しかも、それは1つだけではありません。 FS 特殊変数も同じ規則に従います。

ただし、一般的には、1 つの文字列は 1 つの文字に一致します。したがって、より複雑なものが必要な場合は、フィールド区切り文字が拡張正規表現であることを覚えておく必要があります。

例として、コロンを区切り文字として使用する複数値フィールドであるように見えるグループ フィールドがどのように処理されるかを見てみましょう。

awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest

ユーザーごとに最大 2 つのグループが表示されると予想していましたが、ほとんどのグループでは 1 つのグループのみが表示されます。この問題は、セパレータが複数回出現することが原因で発生します。したがって、解決策は次のとおりです。

awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest

引用符の代わりにスラッシュは、リテラルがプレーンな文字列ではなく正規表現であることを示し、プラス記号は、この式が前の文字の 1 つまたは複数の出現と一致することを示します。したがって、その場合、各区切り文字は 1 つまたは複数の連続するコロン (最長シーケンス) で構成されます。

20. AWKコマンドによる検索と置換

正規表現と言えば、sed s///g コマンドのような置換を 1 つのフィールドに対してのみ実行したい場合があります。この場合に必要なのは gsub コマンドです。

awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest

gsub 関数は、検索する正規表現、置換文字列、および変更されるテキストを含む変数を受け取ります。それ以降が欠落している場合は、$0 とみなされます。

F. AWK での外部コマンドの操作

AWK のもう 1 つの優れた機能は、外部コマンドを簡単に呼び出してデータを処理できることです。これを行うには基本的に 2 つの方法があります。system 命令を使用してプログラムを呼び出す方法と、プログラムの出力を AWK 出力ストリームに混合させる方法です。または、パイプを使用して、AWK が外部プログラムの出力をキャプチャして、結果をより細かく制御できるようにします。

これらはそれ自体大きなトピックかもしれませんが、これらの機能の背後にある力を示すための簡単な例をいくつか紹介します。

21. ファイルの先頭に日付を追加する

awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

その AWK プログラムでは、まず作品 UPDATED を表示します。次に、プログラムは外部日付コマンドを呼び出し、その段階で AWK によって生成されたテキストの直後にその結果を出力に送信します。
AWK プログラムの残りの部分は、最終的にファイル内に存在する更新ステートメントを削除し、すべてを出力するだけです。他の行 (ルール 1 を使用)。

next ステートメントに注目してください。現在のレコードの処理を中止するために使用されます。これは、入力ファイルから一部のレコードを無視する標準的な方法です。

22. フィールドを外部から変更する

より複雑な場合には、| を考慮する必要があるかもしれません。 AWK の getline VARIABLE イディオム:

awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01    dec   2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01    dec   2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01    jan   2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12    jun   2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f

これにより、CMD 変数に保存されているコマンドが実行され、そのコマンドの出力の最初の行が読み取られて、変数 $5 に保存されます。

CMD | を実行するたびに AWK に外部コマンドの新しいインスタンスを作成させるため、ここでは close ステートメントに特に注意してください。 getline ステートメント。 close ステートメントがないと、AWK は代わりに、同じコマンド インスタンスから複数行の出力を読み取ろうとします。

23. 動的に生成されたコマンドの呼び出し

AWK のコマンドは、特別なものを何も持たない単なる文字列です。外部プログラムの実行をトリガーするのはパイプ演算子です。したがって、必要に応じて、AWK 文字列操作関数と演算子を使用して、任意の複雑なコマンドを動的に構築できます。

awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"'  FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest

printf 関数についてはすでに説明しました。 sprintf は非常に似ていますが、構築された文字列を出力に送信するのではなく返します。

24. データの結合

close ステートメントの目的を示すために、最後の例を試してみましょう。

awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin  1e2a4f52
52 01    dec   2018 sonia team  c23d4b65
52 01    dec   2018 sonia team  347489e5
25 01    jan   2019 sonia team  ba985e55
10 01 jan 2019 sylvain team:::admin  81e9a01c
8 12    jun   2018 öle team:support  4535ba30
17 05 apr 2019 abhishek guest  80a60ec8

上記の uuid コマンドを使用した例とは逆に、ここでは od のインスタンスが 1 つだけ 起動されます。 AWK プログラムが実行されており、各レコードを処理するときに、 その同じプロセスの出力をさらに 1 行読み取ります。

結論

AWK の簡単なツアーは、そのツールの本格的なコースやチュートリアルに代わるものではありません。ただし、AWK に詳しくなかった方にとって、AWK をツールボックスにすぐに追加できる十分なアイデアが得られたことを願っています。

一方、すでに AWK の愛好家である場合は、より効率的に、または単に友達を感動させるために使用できるいくつかのトリックをここで見つけたかもしれません。

しかし、私は網羅的であるつもりはありません。したがって、どのような場合でも、下のコメント セクションを使用して、お気に入りの AWK ワンライナーやその他の AWK ヒントを遠慮なく共有してください。