webdevqa.jp.net

シェルスクリプトの比較でx $ VAR = xyesがよく使用されるのはなぜですか?

これは、autotools(autoconf、automake)を使用するプロジェクトのビルドスクリプトでよく見られます。誰かがシェル変数の値をチェックしたいとき、彼らはこのイディオムを頻繁に使用します:

if test "x$Shell_VAR" = "xyes"; then
...

このように値をチェックするだけの利点は何ですか?

if test $Shell_VAR = "yes"; then
...

私はこれを頻繁に見るいくつかの理由があるに違いないと思いますが、それが何であるか理解できません。

58
jonner

simple置換を行うシェルを使用していて、Shell_VAR変数が存在しない(または空白である)場合は、エッジのケースに注意する必要があります。次の変換が行われます。

if test $Shell_VAR = yes; then        -->  if test = yes; then
if test x$Shell_VAR = xyes; then      -->  if test x = xyes; then

testへの最初の引数がなくなっているため、これらの最初のものはエラーを生成します。 2番目にはその問題はありません。

あなたのケースは次のように翻訳されます:

if test "x$Shell_VAR" = "xyes"; then  -->  if test "x" = "xyes"; then

xは、少なくともPOSIX準拠のシェルでは、空の引数とスペースを含む引数の両方が単一のオブジェクトとして解釈されるため、引用符で囲まれているため、実際には冗長です。

83
paxdiablo

まだ誰も言及していないもう1つの理由は、オプション処理に関連しています。あなたが書く場合:

if [ "$1" = "abc" ]; then ...

$ 1の値は '-n'であり、テストコマンドの構文があいまいです。何をテストしていたのか明確ではありません。前面の「x」は、先頭のダッシュが問題を引き起こすのを防ぎます。

Testコマンドが-nまたは-zをサポートしていないシェルを見つけるには、本当に古いシェルを調べている必要があります。バージョン7(1978)のtestコマンドに含まれていました。それはまったく無関係ではありません-バージョン6のUNIXの一部はBSDにエスケープされましたが、最近では、現在使用されているものを見つけるのは非常に困難です。

他の多くの人々が指摘したように、値を二重引用符で囲まないことは危険です。実際、ファイル名にスペースが含まれる可能性がある場合(MacOS XとWindowsはどちらもある程度それを推奨しており、Unixは常にそれをサポートしていますが、xargsのようなツールはそれを難し​​くしています)、ファイルを囲む必要があります名前を使用するたびに二重引用符で囲みます。値を担当しない限り(たとえば、オプションの処理中に、起動時に変数を 'no'に設定し、フラグがコマンドラインに含まれている場合は 'yes'に設定します)、引用符で囲まれていない形式の変数を使用するのは安全ではありません。それらが安全であることを証明するまで、そしてあなたは多くの目的のためにいつでもそうすることもできます。または、ユーザーが名前に空白を含むファイルを処理しようとすると、スクリプトがひどく失敗することを文書化します。 (そして、他にも気になる文字があります-たとえば、バッククォートもかなり厄介な場合があります。)

19

この規則について私が知っている2つの理由があります。

http://tldp.org/LDP/abs/html/comparison-ops.html

複合テストでは、文字列変数を引用するだけでは不十分な場合があります。 [-n "$ string" -o "$ a" = "$ b"]は、$ stringが空の場合、一部のバージョンのBashでエラーを引き起こす可能性があります。安全な方法は、空の変数に追加の文字を追加することです[[x $ string "!= x -o" x $ a "=" x $ b "](" x's "キャンセル)。

次に、Bash以外のシェル、特に古いシェルでは、空の変数をテストする '-z'などのテスト条件が存在しなかったため、次のようになります。

if [ -z "$SOME_VAR" ]; then
  echo "this variable is not defined"
fi

bASHで正常に動作します。デフォルトのシェルがBashであり、-zテスト条件がサポートされているかどうかを確認できないさまざまなUNIX環境間での移植性を目的としている場合は、[[ x $ SOME_VAR "=" x "]これは常に意図した効果があるためです。基本的に、これは空の変数を見つけるための古いシェルスクリプトトリックであり、よりクリーンな方法が利用できるにもかかわらず、現在でも下位互換性のために使用されています。

10
Jay

代わりに私はお勧めします:

if test "yes" = "$Shell_VAR"; then

醜いxがなくなり、 https://stackoverflow.com/a/174288/895245 で言及されている問題が解決されるため、$Shell_VAR-およびオプションとして読み取られます。

私はそれが原因だと信じています

SHELLVAR=$(true)
if test $SHELLVAR  = "yes" ; then echo "yep" ; fi 

# bash: test: =: unary operator expected

と同様

if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi
# bash: test: =: unary operator expected

そして

SHELLVAR=" hello" 
if test $SHELLVAR = "hello" ; then echo "yep" ; fi
# yep 

しかし、これは通常はうまくいくはずです

SHELLVAR=" hello"
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output>

しかし、それがどこか別の場所で出力で不平を言うとき、私が推測していることについて何が不平を言っているのかわかりにくいので、

SHELLVAR=" hello"
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

同様に機能しますが、デバッグが容易になります。

2
Kent Fredric

Shell_VARが未定義である可能性がある場合、私はDOSでこれを使用していました。

0
Ken

"x $ Shell_VAR"を実行しない場合、$ Shell_VARが未定義の場合、 "="がモナド演算子ではないなどのエラーが発生します。

0
Paul Tomblin