シェル変数を文字列リテラルと比較するときに両側にプレフィックスを追加する目的は何ですか?

シェル変数を文字列リテラルと比較するときに両側にプレフィックスを追加する目的は何ですか?

長年にわたって変数とリテラルの前に文字が来る文字列リテラルと変数を比較することが何度もありました。

if [ "x$A" = "xtrue" ]; then

$Aであることを確認するために"true"

これは、シェルの互換性のため、または長期的なバグ、直感的ではない動作などを解決するために行われたと思います。明らかなアイデアはありません。

今日はその理由を知りたいと思いましたが、調査の結果何も出ませんでした。それとも、私はまれな出来事に頻繁にさらされて作られたものかもしれません。

このアプローチはまだ役に立つでしょう、たぶん最高ですか?

ベストアンサー1

ここで理解すべき重要な点は、ほとんどのシェルでは、他の一般的な[コマンドと同様に、シェルで解析される一般的なコマンドにすぎないことです。

その後、シェルは list 引数を使用して (または呼び出される) コマンドを呼び出し、[これを条件式として解釈します。test[

当時、これは単なる文字列のリストであり、どの文字列がどのような拡張で作成されたかに関する情報は、組み込み[シェル(今はBourneのようなシェルである)からも失われました。

ユーティリティ[は、どの引数が演算子であるかオペランド(演算子が処理したもの)であるかを区別するのに苦労しました。構文が本質的にあいまいであることは役に立ちません。たとえば、

  • [ -t ][stdoutが端末かどうかをテストするために使用されました(そしてまだいくつかのシェルにあります)。
  • [ x ][ -n x ]x空でない文字列の省略形をテストします(したがって、上記と競合を確認できます)。
  • 一部のシェルでは[-aおよび両方が単項である可能性-oがあります([ -a file ]アクセス可能なファイル(これで置き換えられました[ -e file ][ -o option ]このオプションは有効ですか?)と二項演算子(そしてそしてまたは)。同様にすること! -a xもできます。and(nonempty("!"), nonempty("x"))not(isaccessible("x"))
  • (、さらに質問を追加し)てください!

Cまたはなどの一般的なプログラミング言語では、次のようになりperlます。

if ($a eq $b) {...}

$aorの内容は、条件式が$b解析され拡張されるため、演算子と見なすことは$aでき$bません。ただし、シェルでは次の場所にあります。

[ "$a" = "$b" ]

シェル拡張変数最初²。たとえば、$a含める($b含めると、)すべてのコマンドは[[および(パラメータを確認します。したがって、これは(語彙的に等しい)または(空でない文字列)を意味しますか?=)]"(" = ")"()( -n = )=

歴史的実装(test1970年代後半のUnix V7に登場)は、単に引数を処理する順序のため、明示的な場合でも失敗した。

PDP11エミュレータのUnixバージョン7は次のとおりです。

$ ls -l /bin/[
-rwxr-xr-x 2 bin      2876 Jun  8  1979 /bin/[
$ [ ! = x ]
test: argument expected
$ [ "(" = x ]
test: argument expected

ほとんどのシェルと[実装には、これらの問題やバリエーションに問題があるか、ありました。現在のバージョンはbash4.4です。

bash-4.4$ a='(' b=-o c=x
bash-4.4$ [ "$a" = "$b" -o "$a" = "$c" ]
bash: [: `)' expected, found =

POSIX.2(90年代初頭発売)デザインアルゴリズムこれにより、最も一般的な使用パターン(たとえば、指定されていない)に渡されるときに[最大4つの引数(および除外[)の動作が明示的かつ決定的に作成されます。 、およびを使用しなくなり、オペランドなしで削除されます。アルゴリズムは実際には2.0で実装されました(または少なくとも試みられました)。][ -f "$a" -o "$b" ]()-a-o-tbashbash

したがって、POSIX[準拠の実装では、比較された合計の内容が[ "$a" = "$b" ]何であっても同じであることが保証されます。そうでない場合は、次のように書きます。$a$b-o

[ "$a" = "$b" ] || [ "$a" = "$c" ]

[つまり、毎回5つ未満のパラメータを使用して2回呼び出されます。

しかし、すべての実装が規制を遵守するのにかなりの時間がかかりました[bash4.4までは互換性がありません。 (最後の問題は、[ '(' ! "$var" ')' ]実際の生活で実際に誰も使用しないことです。)

Solaris 10以前のSolarisシェルは/bin/shPOSIXシェルではありませんが、Bourneシェルにはまだ問題があります[ "$a" = "$b" ]

$ a='!' b='!'
$ [ "$a" = "$b" ]
test: argument expected

で始まる演算子が[ "x$a" = "x$b" ]ないため、を使用するとこの問題を解決できます。別のオプションは、次のものを使用することです。[xcase

case "$a" in
  "$b") echo same;;
     *) echo different;;
esac

$b(aroundではなくaroundを引用する必要があります$a)。

とにかく、null値に関するものではなく、そのようなこともありません。人々は変数を引用することを忘れたときにnull値に問題がありますが、[それは問題ではありません[

$ a= b='-o x'
[ $a = $b ]

デフォルト値は$IFS次のとおりです。

[ = -o x ]

=これはx空でない文字列かどうかに関するテストですが、プレフィックスがどれだけ多くても役に立ちません。[ x$a = x$b ]それでも次のようになります。[ x = x-o x ]これにより、エラーが発生し、DoSや他の値を使用したランダムなコマンド注入を含む状況がさらに悪化する可能性があります。例bash:

bash-4.4$ a= b='x -o -v a[`uname>&2`]'
bash-4.4$ [ x$a = x$b ]
Linux

正しい解決策は常に引用:

[ "$a" = "$b" ]   # OK in POSIX compliant [ / shells
[ "x$a" = "x$b" ] # OK in all Bourne-like shells

expr同様の(またはより悪い)問題があることに注意してください。

expr=両方のオペランドが10進整数のように見えるときに同じ整数であるか、そうでないときに同じ順序であるかをテストするために使用される演算子もあります。

多くの実装ではexpr + = +、 またはexpr '(' = ')'expr index = index同等性を比較しません。expr "x$a" = "x$b"これは文字列比較の問題を解決しますが、プレフィックスを使用するとx順序に影響を与える可能性があります(xたとえば、始まる対照要素を持つロケールでは)、負の比較ではなく数値の比較には使用できませんexpr "0$a" = "0$b"expr " $a" = " $b" 一部の実装では整数比較には機能しますが、他の実装では機能しません(a=01 b=1一部はtrueを返し、一部はfalseを返します)。


1はksh93例外です。 Inはksh93予約語[inと見なすことができ、これは実際には、またはまたは[ -t ]とは異なります。これは、以前のバージョンとの互換性を維持しながらも重要な場合は、POSIX規格を維持するためのものです。リテラルの場合のみ、ここでは演算子と見なされ、コマンドを呼び出していることを検出します。var=-t; [ "$var" ]""[ -t ]cmd='['; "$cmd" -t ]-tksh93[

²kshは、[[...]]この問題を解決するために独自の解析規則(および独自の問題)を使用して条件式演算子を追加します(他の一部のシェルでも見つかりますが、いくつかの違いがあります)。

zsh³除く分割+グローバルパラメータ拡張時には呼び出されませんが、空の除去それでもtrue、または他のシェルで分割+globがグローバルに無効になっている場合set -o noglob; IFS=

おすすめ記事