気が利いてるのか、お節介なのか
ロケール環境変数(LC_*
やLANG
)を認識してくれるGNU版のgrepやsed、AWKコマンドの正規表現は、キャラクタークラスを用いた場合、全角の文字を半角で対応する文字と同一視してくれます。
知ってました?
それを知らずにハマったことのある人には、「なんてお節介な!」と思える機能かもしれませんが、知っていれば便利な機能です。
[[:alnum:]]で試してみる
正規表現には**キャラクタークラス指定**というものがあります。例えば[[:alnum:]]
は、アルファベットと数字にマッチするクラスです。
UTF-8やEUC-JPなど、マルチバイト文字に対応するようにロケール環境変数が設定されていると、sed
やAWK
における[[:alnum:]]
は全角文字にも反応します。
$ echo 'MSX MSX2 MSX2+' | sed 's/[[:alnum:]]/*/g'
*** **** ****+
$ echo 'MSX MSX2 MSX2+' | awk '{gsub(/[[:alnum:]]/,"*");print}'
*** **** ****+
$
というように、行頭にある半角の“MSX”もその後に続く全角の“MSX2”も*
に置換されます。
ちなみに、[]
の中に^
を付ければ意味が反転するわけですが、それもちゃんと効きます。やってみましょう。
$ echo 'MSX MSX2 MSX2+' | sed 's/[^[:alnum:]]/*/g'
MSX*MSX2*MSX2*
$ echo 'MSX MSX2 MSX2+' | awk '{gsub(/[^[:alnum:]]/,"*");print}'
MSX*MSX2*MSX2*
$
というわけで、アルファベットでも数字でもない、全半角スペースとプラス記号が*
に置換されました。
[[:blank:]]で試してみる
もちろん他のキャラクタークラスでも同様です。例えば、半角スペースやタブに反応する[[:blank:]]
というクラスでやってみましょう。
$ echo 'MSX MSX2 MSX2+' | sed 's/[[:blank:]]/*/g'
MSX*MSX2*MSX2+
$ echo 'MSX MSX2 MSX2+' | awk '{gsub(/[[:alnum:]]/,"*");print}'
MSX*MSX2*MSX2+
$
というように、行頭から4文字目の半角スペースもそれ以降にある全角スペースも*
に置換されました。つまり、全角スペースも半角スペースも同一視されていることがわかります。
ですが、AWKのフィールド区切りは飽くまで半角のみなようで、上記文字列の最大フィールド番号(NF)を確認してみると2だったりします。
$ echo 'MSX MSX2 MSX2+' | awk '{print NF}'
2
$
へんなのー。
全角に反応させたくない場合はどうすればいいのか
全角と半角を区別し、半角だけにマッチしたいという場合も当然ありますよね。そういう場合は、環境変数を無効にしてやればOKです。
例えばenv
コマンドに-i
オプションを付けてその後にコマンドを書いてやれば、ロケールを含めて全ての環境変数が渡されなくなりますので全角文字はマルチバイト列と見なされて反応しなくなります。
$ echo 'MSX MSX2 MSX2+' | env -i sed 's/[[:alnum:]]/*/g'
*** MSX2 MSX2+
$ echo 'MSX MSX2 MSX2+' | env -i awk '{gsub(/[[:alnum:]]/,"*");print}'
*** MSX2 MSX2+
$
あるいは、環境変数LANG
をC
にしたり、unsetしたりしてもいいです。
$ echo 'MSX MSX2 MSX2+' | LANG=C sed 's/[[:alnum:]]/*/g'
*** MSX2 MSX2+
$ unset LANG
$ echo 'MSX MSX2 MSX2+' | awk '{gsub(/[[:alnum:]]/,"*");print}'
*** MSX2 MSX2+
$
都合よく使い分けましょう。