はじめに
補足 UTF-8 を期待通りに扱える awk の実装もあります。
- 扱える: gawk、Solaris (
/usr/xpg4/bin/awk
と/usr/bin/awk
) - 扱えない: mawk、original-awk、busybox awk、macOS、FreeBSD 12.0、NetBSD 9.0、OpenBSD 6.6
論より証拠
環境は Ubuntu 20.04.2。original-awk
とは nawk
のこと
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 gawk '{ print length($0) }'
5
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 mawk '{ print length($0) }'
15
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 original-awk '{ print length($0) }'
15
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 busybox awk '{ print length($0) }'
15
$ echo あいうえおかきく | LC_ALL=ja_JP.UTF-8 gawk '{ print substr($0,4,3) }'
えおか
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 mawk '{ print substr($0,4,3) }'
い
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 original-awk '{ print substr($0,4,3) }'
い
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 busybox awk '{ print substr($0,4,3) }'
い
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 gawk '{ gsub(/./, "[&]"); print }'
[あ][い][う][え][お]
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 mawk '{ gsub(/./, "[&]"); print }'
[][][][][][][][][][][][][][][]
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 original-awk '{ gsub(/./, "[&]"); print }'
[][][][][][][][][][][][][][][]
$ echo あいうえお | LC_ALL=ja_JP.UTF-8 busybox awk '{ gsub(/./, "[&]"); print }'
[][][][][][][][][][][][][][][]
こんな状態でも POSIX 準拠だという事実
POSIX は C ロケール(別名 POSIX)以外のロケールを規定していません。しかしロケールを追加することは認めています。追加したロケールでの挙動は決まってないのでどのような動きであっても POSIX 違反にはなりません。
なぜ POSIX で C ロケール以外が規定されていないのかというと、どのようなロケールを実装するかは「OS で自由に拡張して良い」と定義するのが標準規格としては最善の選択で1、さらに少なくとも POSIX 策定当時の 1988 年(シェルとコマンドは 1992 年)時点で、移植性があると認められたロケール(つまりどこでも使えるロケール)が C しかなかったからだと思われます。ちなみに UTF-8 の誕生は 1992 年、公式発表は 1993 年です。
POSIX で C.UTF-8 を標準化しようという提案は、ついこの間の 2022年1月11日 にされたばかりです(0001548: Addition of a POSIX.utf-8 locale (likely as 7.3 "POSIX.utf-8 locale")。反応はまだないようです。
さいごに
この事実は(どの環境でも同じように動く)移植性がある方法で UTF-8 を扱う awk コードを書くのは困難であることを意味しています。awk 用の UTF-8 ライブラリでも作れば可能かもしれませんが。
UTF-8 文字列を正しく扱いたい場合は gawk を使いましょう。もしくは UTF-8 をサポートしている他のコマンドや言語を使いましょう。
FAQ(?) なんでロケールにちゃんと対応しないの? ⇒ ロケールにちゃんと対応すると挙動が変わって互換性問題が発生しますからねぇ
-
POSIX で「ja_JP.UTF-8 を実装しなければならない」と決めてしまうと、ja_JP.UTF-8 を実装してない Unix が POSIX 準拠にならなくなって OS ベンダーを困らせてしまいます。しかし「最低限実装しなければいけないのは C (POSIX) だけ、あとは自由な拡張にまかせる」と決めれば OS ベンダーは困らない上に OS ベンダーが思いついた拡張機能の実装を妨げることがありません。これが POSIX 流の考え方です。 ↩