web-dev-qa-db-ja.com

LC_MESSSAGESを有効にするためにmacOS自作bashにエクスポートする必要があるのはなぜですか?

MacOSで、自作からbashをインストールすると、LC_MESSAGESの設定が現在のシェルのロケール設定に何らかの影響を与えるように見えることに気付きましたが、メッセージはLC_MESSAGESがエクスポートされるまで実際には変更されません。

LANGLC_MESSAGESの設定を解除すると、期待どおりに英語のエラーメッセージが表示されます。

bash-4.4$ unset LANG LC_MESSAGES
bash-4.4$ if :;  fi
bash: syntax error near unexpected token `fi'

LC_MESSAGESを誤った値に設定すると、setlocaleに関するエラーが発生します。

bash-4.4$ LC_MESSAGES=foo
bash: warning: setlocale: LC_MESSAGES: cannot change locale (foo): No such file or directory

したがって、LC_MESSAGESを設定すると、somethingが変化します。ただし、適切な値に設定しても効果はありません。

bash-4.4$ LC_MESSAGES=ja_JP.UTF-8
bash-4.4$ if :;  fi
bash: syntax error near unexpected token `fi'

エクスポートするまで:

bash-4.4$ export LC_MESSAGES
bash-4.4$ if :;  fi
bash: 予期しないトークン `fi' 周辺に構文エラーがあります

(これはすべてLANGにも当てはまるようです。)

Bash変数 に関するBashマニュアルのセクションには、LC_MESSAGESまたはLANGをエクスポートする必要があるとは記載されていません(そして、そこにリストされている他のほとんどの変数をエクスポートする必要はありません)。

どうしてこれなの?

4
user321245

_LC_*_シェル変数を割り当てると、エクスポートされているかどうかに関係なく、bashが対応するカテゴリのPOSIXsetlocale()を変数の値で呼び出すようになります。 LANGの場合、すべての_LC_*_変数に対してsetlocale(LC_ALL, thevalue)に続いてsetlocale(LC_*)を再度呼び出します。 LANGUAGEの場合、何もしません。

現在、bashはGNUプロジェクトのシェルです。テキストのローカリゼーションには、GNU gettext、別名libintlを使用します。独自のものも付属しています。 _--with-included-gettext_でbashスクリプトを呼び出すと、configureでコンパイルできるソースにバンドルされているバージョン。

gettextは、言語ごとのデータベースでメッセージの翻訳を検索します。どの言語であるかは、_LC_MESSAGES_カテゴリの値によって決まりますが、_$LANGUAGE_環境変数で上書きできます。

Gettextのドキュメントによると、前回のsetlocale()の呼び出しは、カテゴリの値を決定するものである必要がありますが、いくつかの問題があります。

マルチスレッドアプリケーションの場合、 現在、gettextがその値を取得するために使用できる標準APIはありませんbashはマルチスレッドアプリケーションではありませんが、 setlocale(category, NULL)が返すものは実装定義であり、実際には常に使用できるとは限りません

したがって、実際には、gettextはsetlocale()を使用して、GNU libcの一部として、またはlibcがGNU libc(GNUシステム)でbashと_--with-included-gettext_を使用して構築されたもののように、信頼できることがわかっているため)。

他のシステムでは、以前にgetenv()がどのように呼び出されたかに関係なく、setlocale()を使用してロケールを決定します。そのため、この動作が表示されます。

これらの変数のエクスポートは簡単な回避策です。それらがエクスポートされない場合、それらはとにかく環境の一部ではないと主張することができます。 POSIXはそれについてあまり明確ではありません。別の見方をすれば、変換はbashではなく、サードパーティのメカニズムによって行われるため、executing他のコマンドの場合と同様に、環境変数を使用してロケール情報を渡す必要があります。 2つのソフトウェア(ここではbashgettext)。

さて、GNUシステムでは、実際には悪化します。

上記のように、gettextはGNU libcに含まれています。_$LANGUAGE_は_$LC_MESSAGE_よりも優先されますが、_$LANGUAGE_はPOSIXロケールAPIの一部ではありません。その上に拡張機能があります。

したがって、GNUシステムでは、gettextはsetlocale(LC_MESSAGES, NULL)を使用してLC_MESSAGESカテゴリの名前を取得しますが、LANGUAGEの場合は、常にgetenv()を使用しますが、LANGUAGEはロケールではありませんカテゴリー。

問題は、bashが、libcの_environ[]_配列から切り離されて、変数処理の一部として環境を単独で管理することです。独自のgetenv()があり、独自のバージョンの環境を照会しますが、gettextがlibcの一部として構築され、bashが動的にリンクされている場合、dgettext()getenv()をから呼び出します。 libcは、bashの呼び出しではなく、libc内の内部呼び出しであるため、bashが開始された時点からのみ_$LANGUAGE_値を取得します。

したがって、GNUシステムでは、bashが静的にリンクされているか、_--with-included-gettext_で構築されていない限り、_$LANGUAGE_への変更は、変数がbashによって生成されたメッセージに対して無視されます。 gettextはlibcの一部ではないため、他のシステムでは(_$LANGUAGE_がエクスポートされている限り)問題ありません。そのため、bashgetenv()が呼び出されます。

Debianの場合:

_$ LANGUAGE=fr bash -c 'LANGUAGE=es; eval fi'
bash: eval: ligne 0: erreur de syntaxe près du symbole inattendu « fi »
bash: eval: ligne 0: `fi'
_

(フランス語のメッセージ、スペイン語ではなく、bashが呼び出されたときの_$LANGUAGE_の値)。

実際、他のシェルではそれほど良くありません。

zshは他の言語に翻訳されていませんが、GNUシステムで内部的にgettextを使用するstrerror()を使用します:

_$ LANGUAGE=fr zsh -c 'LANGUAGE=es; true</x; LANGUAGE=en; true</a; true < /etc/shadow'
zsh:1: no existe el archivo o el directorio: /x
zsh:1: no existe el archivo o el directorio: /a
zsh:1: permission denied: /etc/shadow
_

_LANGUAGE=es_は尊重されましたが、ENOENTの2番目のメッセージが英語で表示されていないことを確認してください(おそらくgettextによってキャッシュされています。_$LANGUAGE_が変更されたときにそのキャッシュは無効になっているはずですが、そうではありませんでした)。

2

シェル変数と環境変数の違いの説明については、 この回答 をご覧ください。本質的に:

シェル変数の設定:

LANG=en_US.UTF-8

環境変数の設定:

export LANG=en_US.UTF-8

シェル変数はシェル専用であり、子プロセスには渡されないため、ロケールの環境変数を設定する必要があります。

0
Richard Barber