awk でシングルクォートを扱う

PrintASingleQuote というドキュメントが AwkChannelWiki (awk の IRC で話題になった問題をまとめているところ) に書かれてあり、非常に面白いので翻訳してみました。

PrintASingleQuoteJapanese に翻訳したものを載せています。 IRC で翻訳して構わないか? と問い合わせたところ、「ここでやらないか?」と言われたので、AwkChannelWiki に載せましたが、こちらにも掲載しておきます。 翻訳はかなりいい加減なところがあるので、時間があれば訂正をしていきたいと思います。

シングルクォートを表示する
========================

この問題は何度も質問される内容なので、議論するのに値する問題です。
この良くある質問は awk の短所そのものを示しているのではありません。むし
ろ、ほとんど常に shell のクォートがシングルクォートと作用しあうことによ
るものなのです。

小話
----

8 進数のエスケープシーケンス ('\047') または printf ('printf "%c", 39')
 を用いてください。
16 進数のエスケープシーケンス ('\x27') を使ってはいけません。
なぜなら、awk の実装による違いがあるのに、括られたテキストと悪いように作
用してしまうからです。

まとまりのない話
---------------

it said 'Hello, World!' という文字列を表示して 0 を返すには、以下のよう
なプログラムで実行できます。

BEGIN {
    print "it said 'Hello, World!' and then returned 0"
    exit 0
}

しかしながら、同じことをコマンドラインで実行すると・・・

awk 'BEGIN{print "it said 'Hello, World!' and then returned 0";exit 0}'

・・・shell は 2 つのシングルクォートの間に挿入されているコマンドの文字
として Hello, World! を解析しようとして文句を言ってきます。

誰かが考え付くかもしれないと思われる最初の考えはシングルクォートよりもダ
ブルクォートの方の中でプログラムの断片を囲むということですが、これは awk
 の文字列そのものの構文や $ フィールド参照演算子と非常にまずく作用しあい
ます。

16 進数エスケープ、それは悪しき呪い
---------------------------------

期待に背いて、次のもっとも明らかな解法、それは 16 進数のエスケープシーケ
ンスですが、これは最初はうまく動作するように見えます。

awk 'BEGIN{print "it said \x27Hello, World!\x27 and then returned 0";exit 0}'

・・・でも、この一貫性は偽りなのです。
gawk, mawk や busybox の awk で以下の断片を実行して、結果を比較してみて
ください。

awk 'BEGIN{print "\x27foo!\x27"}'

mawk と buxybox の awk は予想通りの文字列を表示しますが、gawk はマルチバ
イト文字を出力することに注意してください。
Open Group Base Specifications issue 6 の論理章の 3 段目で記述されている
ように、GNU awk のマニュアルの section 2.2 ("Escape Sequences") で繰り替
えされているように、2 文字以上の連続した 16 進数のバイト列を許可してしま
うので、'\xHH' の 16 進数の記法は曖昧なのです。
不幸なことに 2 バイト以上が与えられた場合、実装に依存した振る舞いになっ
ていまいます。

8 進数は個性が強いけれど・・・
----------------------------

幸運なことに、私たちはいつでもナイフとクマの毛皮だけの生活 (8 進数の生活)
 に戻ることができます。
8 進数のエスケープシーケンスは固定長を持つことが要求されているのです。

awk 'BEGIN{print "\047foo!\047"}'

printf を酷使せよ
----------------

もしくは printf を使うこともできます。

awk 'BEGIN{printf "%cfoo!%c\n", 39, 39}'

・・・しかし、全てのエスケープシーケンスが対応する数字を与えられているこ
とを確認するところから始めなければなりません。
gawk は文字列シーケンスの中の特定の位置によって printf の引数が再利用さ
れるという特徴があります・・・。

awk 'BEGIN{printf "%1$cfoo!%1$c\n", 39}'

・・・しかし、この妥協は礼儀正しいこの社会にはいささか醜いので、言わなかっ
たことにしておきます。

明示的な連接 (おっと、これは私の解答です!)
---------------------------------------

変数にシングルクォート文字を代入してしまうという古い代案があり、明示的な
文字列の連接として使います。

awk 'BEGIN{q="\047";print q"foo!"q}'

・・・しかし、多くのシングルクォートを含むような長い文字列を取り扱う際に
は醜くなります。

正しい方法を使ってください
------------------------

最も清潔な方法はファイルの中でプログラムを書くことです。
クォートの問題で動作するための shell 特有の方法があるかもしれません。
何か他に知っていれば、自由に書き加えてください。

しかし、awker ってどうしてこう分かりにくい表現をするんでしょうかね。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png