【緊急特集】最新の gawk 4.0.0 を追え!

Gawk 4.0.0 Now Available にあるように gawk 4.0.0 がリリースされ、gawk も大きな変化を迎えています。 そこで、gawk 4.0.0 の変更点をできるだけサンプルを使って、おさらいしておきます。

/dev/pid, /dev/ppid, /dev/pgrpid, /dev/user の廃止

/dev/pid, /dev/ppid, /dev/pgrpid, /dev/user はそれぞれ、プロセス ID、親プロセス ID、プロセスグループ ID、ユーザー ID を示し、今までは以下のようにしても取得することができました。 例えば、プロセス ID を取得する場合には以下のようにできます。

#! /usr/local/bin/gawk -f
# pid_old.awk

BEGIN {
    while (getline < "/dev/pid" > 0) {
        print $0;
    }
    close("/dev/pid");
}

既に、gawk 3.1.8 でもワーニングを出力しているので、気づかれている方も多いと思います。

$ gawk3 -f pid_old.awk
gawk: pid.awk:8: warning: use `PROCINFO["pid"]' instead of `/dev/pid'
21299

新しい gawk 4.0.0 では

$ gawk4 -f pid_old.awk

のように何も出力されなくなります。

新しい gawk では以下のようにして、それぞれの値を求めるようになります。

#! /usr/local/bin/gawk -f
# pid.awk

BEGIN {
    print PROCINFO["pid"];
    print PROCINFO["ppid"];
    print PROCINFO["pgrpid"];
    print PROCINFO["uid"];
}

実行してみましょう。

$ gawk4 -f pid.awk
25520
27565
25520
15060

これは前の gawk 3.1.8 などでも有効です。

sub(), gsub() の振る舞いの変化

awk で '\' (バックスラッシュ) と '&' の振る舞いには毎回悩まされる人も多いと思います。 今回 POSIX 2008 に合わせて挙動が変更されています。

以下のようなプログラムを使います。

#! /usr/local/bin/gawk -f
# sub.awk

BEGIN {
    str = "abc";
    sub(/a/, "&", str);
    print str;

    str = "abc";
    sub(/a/, "\&", str);
    print str;

    str = "abc";
    sub(/a/, "\\\&", str);
    print str;

    str = "abc";
    sub(/a/, "\\\\\&", str);
    print str;

    str = "abc";
    sub(/a/, "\\\\", str);
    print str;

    str = "abc";
    sub(/a/, "\\q", str);
    print str;
}

まず、以前の gawk で実行してみます。

$ gawk3 -f sub.awk
abc
&bc
\abc
\&bc
\\bc
\qbc

そもそも予想できましたか? というところは置いておいて、5 番目の挙動に注目して gawk 4.0.0 で実行してみます。

$ gawk4 -f sub.awk
abc
&bc
\abc
\&bc
\bc
\qbc

5 番目の挙動が異なっていることが分かります。 これは gawk.info にも新たに加わっている POSIX に合わせた sub(), gsub() の挙動の変化です。

正規表現として '\s' と '\S' が使えます

最近の言語を使っていると awk の正規表現は貧弱に思えますが、新たに便利な正規表現が加わりました。

  • '\s' はスペースの文字に該当し、'[[:space:]]' と同義です。
  • '\S' はスペースでない文字に該当し、'[^[:space:]]' と同義です。

これは使うと便利そうですね。

split() に 4 番目の引数が使えます

split() はある正規表現で文字列を分割 (split) できるので便利ですが、その区切を何で行ったかを保持してくれる配列を格納できます。

#! /usr/local/bin/gawk -f
# split.awk

BEGIN {
    str = "a b  c:d::e";

    num = split(str, arr, /[ :]+/, arr_sep);

    # 分割された配列
    for (i = 1; i <= num; i++) {
        print "arr[" i "] = " arr[i];
    }

    # 分割した区切の配列
    for (i = 1; i < num; i++) {
        print "arr_sep[" i "] = \"" arr_sep[i] "\"";
    }
}

実行してみます。

$ gawk4 -f split.awk
arr[1] = a
arr[2] = b
arr[3] = c
arr[4] = d
arr[5] = e
arr_sep[1] = " "
arr_sep[2] = "  "
arr_sep[3] = ":"
arr_sep[4] = "::"

このような形で、分割の区切を保持することができます。

文字列をバイトとして扱う

gawk はマルチバイトに対応するため、バイト単位ではなく文字単位で処理を行う方法を取ってきました。 これをオプションでバイト単位に変更できます。

以下のようなプログラムで確認してみます。

#! /usr/local/bin/gawk -f
# char.awk
BEGIN {
   str = "123 abc あいう イロハ";
   print length(str);
   print substr(str, 11);
   num = split(str, arr, "");
   for (i = 1; i <= num; i++) {
       print i " : " arr[i];
   }
}

環境は UTF-8 とします。

$ gawk4 -f char.awk
15
う イロハ
1 : 1
2 : 2
3 : 3
4 :
5 : a
6 : b
7 : c
8 :
9 : あ
10 : い
11 : う
12 :
13 : イ
14 : ロ
15 : ハ

characters-as-bytes を使ってみます。

$ gawk4 --characters-as-bytes -f char.awk
27
<以下は泣き別れによる文字化け>

個人的には起動オプションでの変更ではなく、変数によって切り替えができると 嬉しいんですけどね。

サンドボックスモード

awk を使ったウイルスはないとしても、スクリプトの中に悪意を持ったコードが書かれていることを知らずに実行してしまい、ファイルが消去されてしまうケースはゼロではないでしょう。 具体的には system() や print, printf() を使ったリダイレクションや getline を使ったリダイレクションが該当します。

#! /usr/local/bin/gawk -f
# sandbox.awk

BEGIN {
    print "abc" > "test.txt";
    close("test.txt");

    while (getline < "test.txt" > 0) {
        print $0;
    }

    system("rm test.txt");
}

このようなコードがあったとします。

普通に実行すると 'abc' という文字列を test.txt というファイルに破壊的に書き込み、これを表示します。

$ gawk4 -f sandbox.awk
abc

test.txt というファイルがあると破壊してしまいます。

これを sandbox モードで実行してみます。

$ gawk4 --sandbox -f sandbox.awk
gawk: sandbox.awk:5: fatal: redirection not allowed in sandbox mode

このようにファイルを書き込むことができなくなります。

Indirect 関数のサポート

変数の値を関数名として受け取ってくれたらと思ったことはありませんか? 特にオブジェクト指向でない awk では、そうしたケースがあります。

良い例ではありませんが、じゃんけんに勝つプログラムを作ってみました。 じゃんけんで出した値 (手) によって呼び出される関数を変えています。 Indirect 関数では変数名の前に '@' を付けると変数名の関数が呼び出されます。

#! /usr/local/bin/gawk -f
# indirect.awk

BEGIN {
    for (i = 1; i < 10; i++) {
        if (i % 3 == 0) {
            var = "gu";
            printf("%s -> ", var);
            @var();
        }
        if (i % 3 == 1) {
            var = "choki";
            printf("%s -> ", var);
            @var();
        }
        if (i % 3 == 2) {
            var = "pa";
            printf("%s -> ", var);
            @var();
        }
    }
}

function gu() {
    print "pa";
}
function choki() {
    print "gu";
}
function pa() {
    print "choki";
}

実行してみます。

$ gawk4 -f indirect.awk
choki -> gu
pa -> choki
gu -> pa
choki -> gu
pa -> choki
gu -> pa
choki -> gu
pa -> choki
gu -> pa

gawk.info の中には再帰的に使った Quick Sort の例が出ていますので、参考に見てください。

インターバル正規表現が可能に

今までの gawk ではオプションに '--re-interval' を付けないと、ある文字列が特定の回数繰り返される正規表現がうまく記述できませんでした。 例えば、数字が 5 回繰り返される場合には、

[0-9][0-9][0-9][0-9][0-9]

と記述する必要がありましたが、

[0-9]{5}

で簡潔に記述できるようになりました。

例えば、1 から 100000 までの間に 5 が 4 回連続する数値を抜き出すには以下のようにします。

#! /usr/local/bin/gawk -f
# interval.awk

BEGIN {
    for (i = 1; i <= 100000; i++) {
        if (i ~ /5{4}/) {
            print i;
        }
    }
}

実行してみます。

$ gawk4 -f interval.awk
5555
15555
25555
35555
45555
55550
55551
55552
55553
55554
55555
55556
55557
55558
55559
65555
75555
85555
95555

桁数の区切や IP アドレスの簡易的な認識にも使えそうですね。

gen-po から gen-pot へ

単にオプションの変更です。

以下のようなファイルを用意します。

#! /usr/local/bin/gawk -f
# gen-pot.awk

BEGIN {
    TEXTDOMAIN = "gettext";
    bindtextdomain(".");

    print _"Hello, World!";
}

以下のようにして pot ファイルを作成します。

$ gawk4 --gen-pot -f gettext.awk > gettext.pot

これを編集して、mo ファイルを生成し、適切なディレクトリに格納します。

$ cat gettext.pot
#: gen-pot.awk:8
msgid "Hello, World!"
msgstr "こんにちは世界!"

$ msgfmt gettext.pot

$ mkdir -p ja_JP/LC_MESSAGES

$ cp messages.mo ja_JP/LC_MESSAGES/gettext.mo

ロケールを ja_JP にして実行します。

$ LC_ALL=ja_JP ../gawk -f gen-pot.awk
こんにちは世界!

switch/case が使えます

gawk では configure オプションで --enable-switch を行うと switch/case が使えましたが、Linux などではディストリビューションごとに configure オプションが異なるため、使えたり使えなかったりしていました。 gawk 4.0.0 ではデフォルトで使えます。

3 の倍数の時だけ "aho" にしてみます。

#! /usr/local/bin/gawk -f
# switch.awk

BEGIN {
    for (i = 1; i <= 10; i++) {
        remainder = i % 3;
        switch (remainder) {
            case 0:
                print "aho";
                break;
            default:
                print i;
        }
    }
}

実行してみます。

$ gawk4 -f switch.awk
1
2
aho
4
5
aho
7
8
aho
10

特にオプションの処理などでは効果がありそうです。

BEGINFILE と ENDFILE が使えます

あまり BEGINFILE と ENDFILE の活用する場所が見つからないと思えるかもしれませんが、BEGINFILE はファイルからフィールド分割する前に処理されます。 つまり、単純にオープンだけを試みた状態ですから、ファイルの有無を調べるような場合に使えます。

#! /usr/local/bin/gawk -f
# beginfile.awk

BEGINFILE {
    if (ERRNO) {
        print "File not exist.";
    }
    exit;
}

{
    print $0;
}

ファイルがない場合について実行してみます。

$ gawk4 -f beginfile.awk file_not_exist
File not exist.

つまり、awk でファイルの有無を調べてエラーを出力するような場合に活用できます。

ディレクトリに対してワーニングとなる

入力にファイルではなくディレクトリを指定した場合には、エラーからワーニングになりました。 ただし、--posix, --traditional をし指定すれば、エラーになります。

$ gawk4 '1' directory
gawk: warning: command line argument `directory' is a directory: skipped

$ gawk4 --posix '1' directory
gawk: fatal: cannot open file `ja_JP' for reading (Is a directory)

$ gawk4 --traditional '1' ja_JP
gawk: fatal: cannot open file `ja_JP' for reading (Is a directory)

$ gawk3 '1' directory
gawk: cmd. line:2: fatal: file `ja_JP' is a directory

新しい組込変数 FPAT

セパレーター (FS) ではなく、フィールドそのものの正規表現を FPAT で実現します。

具体的には開発時にも議論された CSV ファイルを扱う場合です。

CSV ファイル (ただし途中での改行は含まない) の FPAT は以下のようになります。

FPAT = "([^,]+)|(\"[^\"]+\")";

以下のような CSV (ダブルクォートで括られたもの) があるとします。

1,"Is this a pen?","No, it isn't."
2,"Is this a pencil?","Yes, it is."

gawk 4.0.0 では以下のように処理します。

#! /usr/local/bin/gawk -f
# fpat.awk

BEGIN {
    FPAT = "([^,]+)|(\"[^\"]+\")";
}

{
    for (i = 1; i <= NF; i++) {
        print i " -> " $i;
    }
}

実行してみましょう。

$ gawk4 -f fpat.awk sample.csv
1 -> 1
2 -> "Is this a pen?"
3 -> "No, it isn't."
1 -> 2
2 -> "Is this a pencil?"
3 -> "Yes, it is."

かなり CSV の取り扱いが楽になったのではないでしょうか?

FPAT に合わせた patsplit()

同様に FPAT のように必要なパターン部の正規表現を使った split() として patsplit() が用意されました。

以下のものは、通常の split() でも処理ができますが、patsplit() で処理してみます。

#! /usr/local/bin/gawk -f
# patsplit.awk

BEGIN {
    time = "2011/07/01 12:31:56";

    num = patsplit(time, arr, "[^/ :]+");

    year    = arr[1];
    month   = arr[2];
    day     = arr[3];
    hour    = arr[4];
    min     = arr[5];
    sec     = arr[6];

    print year " 年 " month " 月 " day " 日 " hour " 時 " min " 分 " sec " 秒";
}

実行してみましょう。

$ gawk4 -f patsplit.awk
2011 年 07 月 01 日 12 時 31 分 56 秒

簡単そうで複雑な区切の場合に使えそうです。

シェバング (shebang) の文字数制限に優しい

シェバング (#!) には文字数制限の厳しい OS もあることから全ての長いオプション (GNU long option) には短い POSIX オプションが設定されました。

$ gawk4
Usage: gawk [POSIX or GNU style options] -f progfile [--] file ...
Usage: gawk [POSIX or GNU style options] [--] 'program' file ...
POSIX options:          GNU long options: (standard)
        -f progfile             --file=progfile
        -F fs                   --field-separator=fs
        -v var=val              --assign=var=val
Short options:          GNU long options: (extensions)
        -b                      --characters-as-bytes
        -c                      --traditional
        -C                      --copyright
        -d[file]                --dump-variables[=file]
        -e 'program-text'       --source='program-text'
        -E file                 --exec=file
        -g                      --gen-pot
        -h                      --help
        -L [fatal]              --lint[=fatal]
        -n                      --non-decimal-data
        -N                      --use-lc-numeric
        -O                      --optimize
        -p[file]                --profile[=file]
        -P                      --posix
        -r                      --re-interval
        -S                      --sandbox
        -t                      --lint-old
        -V                      --version

To report bugs, see node `Bugs' in `gawk.info', which is
section `Reporting Problems and Bugs' in the printed version.

gawk is a pattern scanning and processing language.
By default it reads standard input and writes standard output.

Examples:
        gawk '{ sum += $1 }; END { print sum }' file
        gawk -F: '{ print $1 }' /etc/passwd

IPv6 対応

ついこの前、日本での IPv4 のアドレスが枯渇しましたが、これに合わせるように gawk も IPv6 に対応しました。

今まで、/inet となっていた部分を /inet6 に変更するそうですが、環境が IPv4 のためにテストできませんでした。 なお、今までの /inet はシステムの標準に合わせて切り替わるそうです。

ミスにも優しい

良く間違えるネタとして [[:space:]] と書かずに [:space:] と書いてしまうものがあります。

以前の gawk ではミスしても良しなに判断していたようですが、gawk 4.0.0 ではワーニングになります。

$ echo 'a b c' | gawk4 '/[:space:]/'
gawk: cmd. line:1: warning: regexp component `[:space:]' should probably be `[[:space:]]'
a b c

$ echo 'a b c' | gawk4 '/[[:space:]]/'
a b c

$ echo 'a b c' | gawk3 '/[:space:]/'
a b c

$ echo 'a b c' | gawk3 '/[[:space:]]/'
a b c

デバッガー dgawk の搭載

awk スクリプトも大規模になるとデバッガーが必要になってきます。 今までは printf() などで出力していたと思いますが、デバッガーが使えるようになり、便利になりました。

例えば以下のようなプログラムがあるとします。

#! /usr/local/bin/gawk -f
# dgawk.awk

BEGIN {

    a = 12;
    b = 5;
    c = a / b;

    div = abs(c);

    print div;
}

function abs(num) {
    if (num >= 0) {
        return num;
    } else {
        return -num;
    }
}

デバッガーから起動してみます。

$ dgawk -f dgawk.awk
dgawk>

まず、ブレークポイント (b) を abs() にしてみます。

dgawk> b abs
Breakpoint 1 set at file `dgawk.awk', line 16

ブレークポイントまで実行 (r) します。

dgawk> r
Starting program:
Stopping in BEGIN ...
Breakpoint 1, abs(num) at `dgawk.awk':16
16          if (num >= 0) {

引数となっている num に入っている値を表示 (p) します。

dgawk> p num
num = 2.3999999999999999

終了 (q) します。

dgawk> q
The program is running. Exit anyway (y/n)? y

使い方としてはこんな感じでしょうか。

詳細は gawk.info を参照してみてください。

break, continue がループの外では無効

今までは break, continue はループの外で使った場合、break や continue に達するとエラーになっていましたが、gawk 4.0.0 からは即時エラーになります。

#! /usr/local/bin/gawk -f
# break.awk

BEGIN {
    for (i = 1; i <= 100; i++) {
        print i;

        if (i % 10 == 0) {
            break;
        }
    }

    break;
}

今までであれば、

$ gawk3 -f break.awk
1
2
3
4
5
6
7
8
9
10
gawk: break.awk:14: fatal: `break' outside a loop is not allowed

のように表示されますが、gawk 4.0.0 では即時エラーになります。

$ gawk4 -f break.awk
gawk: break.awk:13: error: `break' is not allowed outside a loop or switch

多次元配列のサポート

今までちゃんとした多次元配列は awk にはありませんでした。 単に "\034" で連接した 1 次元配列でしたが、gawk 4.0.0 では多次元配列が加わります。

#! /usr/local/bin/gawk -f
# array.awk

BEGIN {
    arr[1, 2] = 2;
    arr[1][3] = 3;

    print "arr[1][2] = " arr[1][2];
    print "arr[1, 2] = " arr[1, 2];
    print "arr[1][3] = " arr[1][3];
    print "arr[1, 3] = " arr[1, 3];
}

実行してみると分かりますが、今までの配列と異なることが分かります。

$ gawk4 -f array.awk
arr[1][2] =
arr[1, 2] = 2
arr[1][3] = 3
arr[1, 3] =

また、split() で配列の配列を作成することもできますが、最初に空の配列を作成する必要があり、少しだけ注意が必要です。

#! /usr/local/bin/gawk -f
# array2.awk

BEGIN {
    arr[1][1] == "";                    # 必要

    num = split("a b c d e", arr[1]);

    for (i = 1; i <= num; i++) {
        print arr[1][i];
    }
}

この空の配列を作成しないとエラーになります。

$ gawk4 -f array2.awk
a
b
c
d
e

文字範囲を示す正規表現が変わります

[a-z] は小文字と思っていたら、大文字にもマッチしたという経験はありませんか?

少し変な気がしますが、[a-z] は小文字だけにマッチするようになります。

#! /usr/local/bin/gawk -f
# range.awk

BEGIN {
    str1 = "abcde";
    str2 = "ABCDE";

    if (str1 ~ /[a-z]/) {
        print str1 " is lower case.";
    }

    if (str2 ~ /[a-z]/) {
        print str2 " is lower case.";
    }
}

今までの gawk では以下のようになっていました。

$ gawk3 -f range.awk
abcde is lower case.
ABCDE is lower case.

gawk 4.0.0 では以下のようになります。

$ gawk4 -f range.awk
abcde is lower case.

リリースノートにも書かれていますが、変だという質問が多かったのでしょう。

PROCINFO"strftime" には strftime() のフォーマットを保持します

awk は変数や配列に値を破壊的に代入できますので、以下のようなこともできます。

#! /usr/local/bin/gawk -f
# starftime.awk

BEGIN {
    print PROCINFO["strftime"] " : " strftime();

    PROCINFO["strftime"] = "%Y/%m/%d";
    print PROCINFO["strftime"] " : " strftime();
}

実行してみましょう。

$ gawk4 -f strftime.awk
%a %b %e %H:%M:%S %Z %Y : Mon Jul  4 09:54:37 JST 2011
%Y/%m/%d : 2011/07/04

for in arr の順序を指定できる

gawk の隠しコマンド的なもので環境変数 WHINY_USERS を 1 にしておくと for in arr の順序をソートしてくれるというものがありましたが、gawk 4.0.0 では順序を指定することができます。

#! /usr/local/bin/gawk -f
# sort_in.awk

BEGIN {
    arr[1]      = 10;
    arr[2]      = 2;
    arr[3]      = "awk";
    arr["abc"]  = 1;
    arr["def"]  = 100;
    arr["ghi"]  = "test";

    # インデックスの文字列昇順
    print "=========== ind_str_asc";
    PROCINFO["sorted_in"] = "@ind_str_asc";
    for (i in arr) {
        print i " : " arr[i];
    }

    # インデックスの数値昇順
    print "=========== ind_num_asc";
    PROCINFO["sorted_in"] = "@ind_num_asc";
    for (i in arr) {
        print i " : " arr[i];
    }

    # 値の型昇順
    print "=========== val_type_asc";
    PROCINFO["sorted_in"] = "@val_type_asc";
    for (i in arr) {
        print i " : " arr[i];
    }

    # 値の文字列昇順
    print "=========== val_str_asc";
    PROCINFO["sorted_in"] = "@val_str_asc";
    for (i in arr) {
        print i " : " arr[i];
    }

    # 値の数値昇順
    print "=========== val_num_asc";
    PROCINFO["sorted_in"] = "@val_num_asc";
    for (i in arr) {
        print i " : " arr[i];
    }
}

実行してみます。

$ gawk4 -f sorted_in.awk
=========== ind_str_asc
1 : 10
2 : 2
3 : awk
abc : 1
def : 100
ghi : test
=========== ind_num_asc
abc : 1
def : 100
ghi : test
1 : 10
2 : 2
3 : awk
=========== val_type_asc
abc : 1
2 : 2
1 : 10
def : 100
3 : awk
ghi : test
=========== val_str_asc
abc : 1
1 : 10
def : 100
2 : 2
3 : awk
ghi : test
=========== val_num_asc
3 : awk
ghi : test
abc : 1
2 : 2
1 : 10
def : 100

これは便利そうですね。 もちろん、逆順も指定できますが、gawk.info を参照してください。

さらに比較関数を独自に作って比較することもできるようです。

配列かどうかを調べられます

配列かどうかを調べるというよりも、gawk 4.0.0 で加わった多次元配列を走査するような場合に役立つでしょう。

#! /usr/local/bin/gawk -f
# isarray.awk

BEGIN {
    arr[1]          = 1;
    arr[2][2]       = 2;
    arr[2, 3]       = 3;
    arr[3][4, 5]    = 4;
    arr[4][5][6]    = 5;

    walk_array(arr, "arr");
}

function walk_array(arr, name,    i) {
    PROCINFO["sorted_in"] = "@ind_num_asc";

    for (i in arr) {
        if (isarray(arr[i])) {
            walk_array(arr[i], (name "[" i "]"))
         } else {
            printf("%s[%s] = %s\n", name, i, arr[i])
         }
    }
}

実行してみます。

$ gawk4 -f isarray.awk
arr[1] = 1
arr[2][2] = 2
arr[23] = 3
arr[3][45] = 4
arr[4][5][6] = 5

asort(), asorti() はどのようにソートするかを決められる

asort() と asorti() は gawk で導入されているソート関数ですが、決まった動作しかできないため、自前でソート関数を作る人も多かったのではないでしょうか?

先ほどの PROCINFO で指定した for in arr の順序指定と同じものが使えます。

#! /use/local/bin/gawk -f
# asort.awk

BEGIN {
    for (i = 1; i <= 10; i++) {
        arr[i] = sin(i);
    }

    print "========== val_num_asc"
    asort(arr, new_arr, "@val_num_asc");

    for (i = 1; i <= 10; i++) {
        print i " : " new_arr[i];
    }

    print "========== val_num_desc"
    asort(arr, new_arr, "@val_num_desc");

    for (i = 1; i <= 10; i++) {
        print i " : " new_arr[i];
    }
}

実行してみます。

$ gawk4 -f asort.awk
========== val_num_asc
1 : -0.958924
2 : -0.756802
3 : -0.544021
4 : -0.279415
5 : 0.14112
6 : 0.412118
7 : 0.656987
8 : 0.841471
9 : 0.909297
10 : 0.989358
========== val_num_desc
1 : 0.989358
2 : 0.909297
3 : 0.841471
4 : 0.656987
5 : 0.412118
6 : 0.14112
7 : -0.279415
8 : -0.544021
9 : -0.756802
10 : -0.958924

最後に

駆け足でしたが、gawk 4.0.0 の新機能を見てきました。 いかがでしたでしょうか?

確実に便利になっているのですが、今までの awk との互換性を考えるとなかなか使えない機能が多い気がします。 機会があればイベントなどで gawk 4.0.0 の機能を紹介したいと思います。

tag_gawk.png