ここひと月ぐらい割とawkを使うようになり、自分なりにmanを読んだり、試行錯誤したawkスクリプトのデバック方法のメモ。
環境
基本的には以下のバージョンのgawkで試している。
[mk55@localhost ~]$ awk --version | head -n 1 GNU Awk 4.0.2
gawkのデバックオプションを使う
gawk
の-d
オプション(もしくは--dump-variables
)を使うと組み込み変数を含むawkの変数の最終的な値をファイルに出力できる。
以下のようなサンプルスクリプト1_debug.awk
があったとすると
{ baz="baz" ; print $1, baz ; }
デバックオプション-d
をつけると以下のように変数の内容をダンプしたawkvars.out
がカレントディレクトリに生成される
[mk55@localhost ~]$ echo "foo bar" | gawk -d -f 1_debug.awk foo baz #変数のダンプファイル。 #組み込み変数とともに、最終行に定義した変数bazの値が出力されている。 [mk55@localhost ~]$ cat awkvars.out ARGC: 1 ARGIND: 0 ARGV: array, 1 elements BINMODE: 0 CONVFMT: "%.6g" ERRNO: "" FIELDWIDTHS: "" FILENAME: "-" FNR: 1 FPAT: "[^[:space:]]+" FS: " " IGNORECASE: 0 LINT: 0 NF: 2 NR: 1 OFMT: "%.6g" OFS: " " ORS: "\n" RLENGTH: 0 RS: "\n" RSTART: 0 RT: "\n" SUBSEP: "\034" TEXTDOMAIN: "messages" baz: "baz"
manにも書いてある通り、typoで変な変数を定義していたり、ループの添え字(i, j)が意図しない値になっていたりすることを検出できることがある。
なお、以下のようにファイルを指定して出力することもできる。
[mk55@localhost ~]$ echo "foo bar" | gawk -d/tmp/debug.txt -f 1_debug.awk foo baz [mk55@localhost ~]$ ls /tmp/debug.txt /tmp/debug.txt [mk55@localhost ~]$ head /tmp/debug.txt ARGC: 1 ARGIND: 0 ARGV: array, 1 elements BINMODE: 0 CONVFMT: "%.6g" ERRNO: "" FIELDWIDTHS: "" FILENAME: "-" FNR: 1 FPAT: "[^[:space:]]+" [mk55@localhost ~]$
printデバックする
AWK Users JP :: awk 基礎文法最速マスターにある通り、awkの実装によらずデバックできるのでprintデバック(もしくはprintfデバック)はよく使うと思う。 ただ、単純にprintを仕込むとデバック用途なのか、通常の用途なのかわからないので一工夫する。
以下のようなサンプルスクリプト2_debug.awk
があったとする。
{ baz="baz" ; if(debug) print "[debug] baz is :" baz > "/dev/stderr" ; print $1, baz ; }
上のスクリプトは通常時は1_debug.awk
と同じ処理をするが、変数debug
が0以外で定義されている場合は標準エラー出力に対してprint文で文字列を出力する*1。
これを利用して、変数を定義できる-v
(もしくは--assign
)オプションを疑似的なdebug print用のオプションのように使う*2*3
#変数debugがないので通常の出力のみ [mk55@localhost ~]$ echo "foo bar" | gawk -f 2_debug.awk foo baz #変数debugが1なのでデバック出力あり [mk55@localhost ~]$ echo "foo bar" | gawk -v debug=1 -f 2_debug.awk [debug] baz is :baz foo baz
上記では変数の出力だけなのでありがたみがないが、例えばBEGINFILE
やENDFILE
等に記載すれば、どのファイルの処理を開始/終了したか出力したりもできる。
なお、本番向けスクリプトにdebug用の記述が入るのが不快ならsedで一括置換してしまえばよい*4。
[mk55@localhost ~]$ sed -i 's/if(debug)/#if(debug)/g' 2_debug.awk [mk55@localhost ~]$ cat 2_debug.awk { baz="baz" ; #if(debug) print "[debug] baz is :" baz > "/dev/stderr" ; print $1, baz ; } [mk55@localhost ~]$ echo "foo bar" | gawk -v debug=1 -f 2_debug.awk foo baz
もし何度も上記を呼ぶような大きなスクリプトを書くなら、いっそ関数化する手もありそうではある*5。
{ baz="baz" ; printDebugMsg( "baz is : " baz ) print $1, baz ; } function printDebugMsg(msg) { if(debug) print "[debug]" msg > "/dev/stderr" }
gawkの互換性チェックのオプションを使う
あまりないとは思うが、awkの処理を処理系の異なるawkに移植する際は、事前に--lint
オプションを使って動かしておくと他の処理系で動作しなさそうな箇所を警告してくれる。
以下の例はgawkにはあり、nawkにはない関数systime()
とstrftime()
を使った処理で--lint
を使ってみた場合。
[mk55@localhost ~]$ echo "now is" | gawk '{print $0 " " strftime("%Y/%m/%d", systime()) }' now is 2017/05/28 [mk55@localhost ~]$ echo "now is" | gawk --lint '{print $0 " " strftime("%Y/%m/%d", systime()) }' gawk: cmd. line:1: warning: `strftime' is a gawk extension gawk: cmd. line:1: warning: `systime' is a gawk extension now is 2017/05/28
gawk付属のプロファイラを使う
gawkにはPAWK
というプロファイラがついているので、これを利用して解析することもできる。
pawk
コマンドを使うか--profile
もしくは-p
オプションを利用すると使用できる
例えば以下のようなことができる。
- BIGINやEND、funcitionなどを種類ごとにまとめて表示*6
- ループ処理が何回呼ばれたか表示
- パターンやif-else分岐が何度呼ばれたか表示
細かい利用方法は以下を読めばだいたいわかる。 The GNU Awk User’s Guide: Profiling
gawk付属のデバッガを使う
gawkにはDAWK
というデバッガがついているのでこれを使うという手もある。
The GNU Awk User’s Guide: Debugging
対話的にコマンド入力したりしてブレークポイントを設定したりなどいろいろできるようだ。 ただ、私自身はデバッガが必要なほど複雑なスクリプトを作ったことはないので、いままでお世話になったことはない。 なので、正直なところ良く分からない。
日本語だとかろうじてこのページでちょっと触れられているぐらいではAWK Users JP :: 【緊急特集】最新の gawk 4.0.0 を追え!
参考
The GNU Awk User’s Guide: Index ⇒gawkのマニュアルページ
AWK 処理系の比較 | YOSBITS ⇒nawk, gawk, mawkの比較。mawkというものがあるのは初めて知った。
Man page of STRFTIME
⇒C言語のstrftime()
のman。awkでも同じような動きをしているようだ。
*1:標準エラー出力に出力するのは、標準出力と区別することでawkの結果と別に出力できるようにするため。例えば、awkの結果をリダイレクトでファイルに出す場合などでも、debug printはコンソール上に表示したり、別のファイルに出力できたりする
*2:Powershellの-DebugとWrite-Debugのようなイメージ
*3:nawkでも-vオプションは利用可能
*4:個人的にはそこまで性能的に問題になることは少ないと思うので、2重に管理するぐらいなら残した方が良いと思うが
*5:awkの関数ってキャメルケースで良いのだろうか?print_debug_msgとかのほうが適切な気もするが…適切な既存の規約を見つけられなかった
*6:たとえば2つのBIGINが書かれてしまっているときの解析に便利