テックブログ

awk のススメ

お久しぶりです。H.Uでございます。

今回は使えるとかなり便利ですが、

覚えるまでが 難解な awk をご紹介したいと思います。

導入

awk(オーク)について、簡単なご紹介から始めます。

awk は文字をフィルタリングする際にコマンドとして一般的に利用されており、

弊社でもログ調査やシェルスクリプト作成など様々な業務で使用しております。

コマンドとして利用する時が多いですが、実際はプログラミング言語の一つです。

言語というだけあり自由度は高いですが、文法はかなりシンプルです。

今回は基本文法から始めて、応用までを考えていきたいと思います。

基本文法

基本文法について記載します。

awk ‘ パターン {アクション}

「パターンにマッチした行に対してアクションを実行する 」

これだけです。

複雑な処理が実施できるわりに、実にシンプルですね。

さらに、パターンやアクションは省略することもできます。

省略ルール

  • パターン を省略すると、全ての行 (レコード) に対してアクションを実行
  • {アクション} を省略すると、{print $0} をアクションとして実行

文章だけで見ると、簡単なのか ?、難しいのか ?、どちらかわかりづらいように感じます。

使用してみることで、確認してみましょう。

基本文法の確認

まずは、先述した文法と比較できるように、省略しないでコマンドを入力してみます。

[root@hujiie-test2 ~]# echo "test" | awk '$0 == "test" {print $0}'
test

既にプログラミング言語に近い形に見え複雑そうですが、分解しながら考えてみます。

$0 は全レコードという意味 
    (※ レコードは「行」と考えてください。)


・    =      →「右辺の値を左辺の変数に入れます 」  (代入) という演算子
        ==    →「右辺の値と左辺の値は同じですか?」(同じか比較する) という演算子


・    パターン      :  全レコードが、"test" ==  (同じだったら)
{アクション}  :  {print $0} を実行 (全レコードを表示する)

パターンにマッチしたので、アクションが実行され、test という結果として表示されました。

逆にパターンにマッチしない場合はどうでしょうか。

[root@hujiie-test2 ~]# echo "test" | awk '$0 == "not-test" {print $0}'
[root@hujiie-test2 ~]#

パターンにマッチしなかったので、アクション(全レコードを表示) が実行されませんでした。

複雑なコマンドに見えても 「awk ‘ パターン {アクション}(基本文法) から考えれば、

理解しやすくなると思います。

次の章からは応用編です。

awk の応用 (フィールド)

冒頭でも述べましたが、awk で一番利用されているのは

フィルタリング ( 特定の行の抜き出し ) として、ではないでしょうか。

いきなりフィルタリングから説明したいのですが、

まず フィールド についての理解が必要です。

フィールドとはどういったものなのでしょうか、確認していきます。

[root@hujiie-test2 ~]# echo "N E T A S S I S T" | awk '{print $0}'
N E T A S S I S T

文字ごとに 空白で区切られた 「 N E T A S S I S T 」という 文字列 に

・   パターン     :  全レコードに対して (省略ルール により、記述が必要ない)

・{アクション}  :  {print $0} を実行(全レコードを表示する)

という awk が実行されています。

次に、下記のような awk を試してみます。

[root@hujiie-test2 ~]# echo "N E T A S S I S T" | awk '{print $1, $3, $7, $NF}'
N T I T

{アクション} に含まれる$1,$3,$7 そして $NF とは何でしょうか。

実はこれらが 「フィールド」と呼ばれているものです。

$1 : 第一フィールド 、$2 : 第二フィールド……. $7 : 第七フィールド…….

のように「空白文字」を区切りとした文字列ごとが「フィールド」です。

また、 $NF は (Number of Field) 「現在のフィールド数」であり、

全文字数が 9 文字 なので、今回は $9 に当たります。

(※awk のデフォルト区切り文字は ” ” )

フィールドを応用することで、自分が表示したい部分だけを出力させることができます。

これをフィルタリングと呼びます。

最後に 次の章でフィルタリングを利用してみます。

awk の応用 (フィルタリング)

例えば、ps コマンドから 出力した内容 を CPU使用率順 に上から10 行まで並べたとします。

[root@hujiie-test2 ~]# ps auxwww --sort -%cpu | head
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      154513  0.2  0.6 560952 27196 ?        Ssl   2022  93:22 /usr/bin/python3.6 -s /usr/bin/fail2ban-server -xf start
root       17372  0.1  0.7 492336 31056 ?        Ssl   2022  76:18 /usr/libexec/platform-python -Es /usr/sbin/tuned -l -P
root           1  0.0  0.2 238424 11240 ?        Ss    2022   0:11 /usr/lib/systemd/systemd --system --deserialize 21
root           2  0.0  0.0      0     0 ?        S     2022   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<    2022   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I<    2022   0:00 [rcu_par_gp]
root           6  0.0  0.0      0     0 ?        I<    2022   0:00 [kworker/0:0H-events_highpri]
root           9  0.0  0.0      0     0 ?        I<    2022   0:00 [mm_percpu_wq]
root          10  0.0  0.0      0     0 ?        S     2022   0:00 [rcu_tasks_rude_]

ですが、確認したい内容は

単純にどのプロセスが、どれだけCPUを使用しているか ? だったとします。

そこで利用するのが、awk フィルタリング です。

[root@hujiie-test2 ~]# ps auxwww --no-headers --sort -%cpu | head | awk '{c="";for(i=11;i<=NF;i++) c=c $i" "; print "ps="c "cpu="$3 "%" }'

ps=/usr/bin/python3.6 -s /usr/bin/fail2ban-server -xf start cpu=0.2%
ps=/usr/libexec/platform-python -Es /usr/sbin/tuned -l -P cpu=0.1%
ps=/usr/lib/systemd/systemd --system --deserialize 21 cpu=0.0%
ps=[kthreadd] cpu=0.0%
ps=[rcu_gp] cpu=0.0%
ps=[rcu_par_gp] cpu=0.0%
ps=[kworker/0:0H-events_highpri] cpu=0.0%
ps=[mm_percpu_wq] cpu=0.0%
ps=[rcu_tasks_rude_] cpu=0.0%
ps=[rcu_tasks_trace] cpu=0.0%

どのプロセスが、どれだけのCPU使用率を利用しているか、ハッキリ出力されていますね。

敢えて{アクション} の中身を複雑にしていますが、かなり応用が利く構文となっています。

先程と同じように分解して解説していきます。

まず、{アクション} の中身の一部である下記をご覧ください。

  • c=””;for(i=11;i<=NF;i++) c=c $i” “;

プログラミング言語の一つだけあって、for 文が登場していますね。

これは単純にフィールドごとをフィルタリングするだけでは、

詳細なプロセスの内容が途中で切れてしまうので、

プロセス部分が初めて出力される 第十一フィールド から $NF までを

for 文を利用してフィルタリングしています。

内容としては、フィールドのフィルタリングに過ぎません。

( ※ 「 ; 」は、複数のawk 文を一行に記述するために利用されます。)

次に、

  • print “ps=”c “cpu=”$3 “%”

ここはfor 文で得た出力結果 c と

$3 : 第三フィールド (ps コマンド %CPU の部分) を print で出力しているだけです。

解説は以上です。

どうでしょうか、少し難解だったかもしれません。

敢えて複雑に記述してあります。

なぜ、そのようなことをしたかを次の章でご説明いたします。

まとめ

理由は単純で、別の awk と連携させやすいようにです。

フィルタリングできるコマンドは、他にいくらでもあります。

なぜ多くの人に awk が利用されているのか、

おそらく 別の awk と 連携させやすいからだと考えています。

もちろん、それ以外もたくさんあると思いますが、

連携させやすく、

かつ様々な変数、関数が用意されている awk は重宝します。

今回は氷山の一角ですがawkのご紹介でした。

最後まで読んでいただき、ありがとうございます。

H.Uでした。

この記事をシェアする

  • facebook
  • twitter
  • hatena
  • line
URLとタイトルをコピーする

実績数30,000件!
サーバーやネットワークなど
ITインフラのことならネットアシストへ、
お気軽にご相談ください