CODE Polyglot / 備忘録

Mac の bash で "$TAG(" と書くと、全角の括弧の1バイト目が変数名に入る

Mac の bash は、UTF-8 の設定のとき、変数のすぐ後ろの全角文字の1バイト目を変数名の一部として読みます。set -u なら「unbound variable」で止まり、無ければ文字が化けたまま出力されるので、全角文字に接する変数は ${TAG} と波かっこで囲みます。

起きたこと

デプロイの手順書(bash)の表示の行 echo "== コンテナ($TAG)" で、set -u のスクリプトが止まりました。エラーの変数名の後ろに、化けた1文字が付いていました。

再現の手順

$ cat t1.sh
set -u
TAG=abc
echo "== コンテナ($TAG)"
echo "== braces(${TAG})"
$ /bin/bash t1.sh
t1.sh: line 3: TAG�: unbound variable

set -u が無いと止まらず、括弧の残りの2バイトだけが出力されます(od -c で見ると、[ と ] の間に 0xBC 0x88 の2バイトだけが残る)。

$ printf 'TAG=abc\necho "[$TAG(]"\n' > t2.sh
$ /bin/bash t2.sh | od -c
0000000    [ 274 210   ]  \n

原因

「(」は UTF-8 で 0xEF 0xBC 0x88 の3バイトです。Mac の UTF-8 のロケールでは、bash が 0x80 以上のバイトを変数名に使える文字とみなし、$TAG の後ろの 0xEF までを変数名 TAG\xEF として読みます。そういう変数は無いので、set -u ではエラーに、無ければ空になり、残りの 0xBC 0x88 だけが出力されます。

直し方

全角の文字に接する変数は、波かっこで囲みます。

echo "== コンテナ(${TAG})"

スクリプト全体を LC_ALL=C で動かしても起きませんが、ほかの日本語の処理に影響するので、波かっこで囲む方を勧めます。

確かめたこと

環境$TAG(${TAG}(
macOS 26.6・/bin/bash 3.2.57・LANG=C.UTF-8止まる正しく出る
macOS 26.6・bash 5.2.37(Homebrew)・LANG=C.UTF-8/ja_JP.UTF-8/en_US.UTF-8止まる正しく出る
macOS 26.6・上の2つ・LC_ALL=C正しく出る正しく出る
Debian 13(Linux)・bash 5.2.37・LANG=C.UTF-8正しく出る正しく出る

同じ bash 5.2.37 でも、Linux では起きませんでした。Linux で動かしたスクリプトを Mac で動かすと、ここで初めて止まることがあります。