よくawkで処理中にOSのコマンドを実行させる事があるので、備忘で残しておく。

1. ただコマンドを実行させる

ただOSのコマンドを実行するだけで良いのであれば、system()を使用すればいい。

awk '{system("OSのコマンド")}'
blacknon@BS-PUB-UBUNTU-01:~$ cat test2.txt
a01 15652
a02 13411
a03 2597
a04 8083
a05 11068
a06 17737
a07 32745
a08 22546
a09 32608
a10 32074
a11 17574
a12 8745
a13 2201
a14 7521
a15 9968
a16 12236
a17 19420
a18 30606
a19 21637
a20 16093
blacknon@BS-PUB-UBUNTU-01:~$ cat test2.txt | awk '{system("echo "$2"|cut -c 1-3")}'
156
134
259
808
110
177
327
225
326
320
175
874
220
752
996
122
194
306
216
160

ただ、このやり方だと残念ながら実行結果をawk内の変数に代入できず(exit codeが代入されてしまう)、かつprintでも扱えない(指定するとexit codeがprintされてしまう)。

blacknon@BS-PUB-UBUNTU-01:~$ cat test2.txt | head | awk '{print $1,system("echo "$2"|cut -c 1-3")}'
156
a01 0
134
a02 0
259
a03 0
808
a04 0
110
a05 0
177
a06 0
327
a07 0
225
a08 0
326
a09 0
320
a10 0

また、こちらを見ると入出力streamを開いてしまうので、実行時に明示的にcloseしてやらないとファイルディスクリプタを消費してしまう様子。

2. コマンドの実行結果をawk内で変数に代入する

awkからOSのコマンドを実行し、その結果を変数に代入する場合、以下のようにgetlineを利用する。

awk '{"OSのコマンド" | getline var}' # 変数varにOSコマンドの実行結果が代入される
blacknon@BS-PUB-UBUNTU-01:~$ cat test2.txt | head | awk '{"echo "$2"|cut -c 1-3" | getline t;print $1,t}'
a01 156
a02 134
a03 259
a04 808
a05 110
a06 177
a07 327
a08 225
a09 326
a10 320

なお、OSコマンドの実行結果が複数行になる場合、変数に代入されるのは最初の1行だけになるようなので、そこは注意が必要になるようだ。 複数行が必要になる場合、OSコマンドの実行結果は一行にし、あとでawk内で分解して扱うのが良さそうだ。

blacknon@BS-PUB-UBUNTU-01:~$ cat test2.txt | head | awk '{"echo "$2"|cut -c 1-3|grep -o ." | getline t;print $1,t,length(t)}'
a01 1 1
a02 1 1
a03 2 1
a04 8 1
a05 1 1
a06 1 1
a07 3 1
a08 2 1
a09 3 1
a10 3 1