文字列が左括弧の場合のシェルブラケットテストのエラー


27

シェルが文字列を解析するのを避けるために、文字列を引用することは常に良い習慣であるという事実に自信を持っていました。

それから私はこれに出くわしました:

$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1

同じエラーを取得して、問題を特定しようとしています:

$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1

私はこのような問題を解決しました:

[ "$x" = '1' ] && [ "$y" = '1' ]

それでも、ここで何が起こっているのかを知る必要があります。


2
回避策として、bashでは、次を使用できます[[ "$x" = '1' && "$y" = '1' ]]
terdon

3
テストのPOSIX仕様では、この理由(仕様の横にある上付き文字が意味するもの)のために明示的に記述され、陳腐化したものとして記述され-a-o[OB]ます。[ "$x" = 1 ] && [ "$y" = 1 ]代わりに記述した場合、問題はなく、明確に定義/標準化された動作の範囲内に収まります。
チャールズダフィー

6
これが、人々が[ "x$x" = "x1" ]引数が演算子と誤解されるのを防ぐために使用した理由です。
ジョナサンレフラー

@JonathanLeffler:ねえ、あなたは私の答えを一文に凝縮した、公平ではない!:) dashBashではなくPOSIXシェルを使用している場合でも、それは便利な方法です。
公称動物

私の質問に答えてくれて時間を割いてくれたみんなに感謝したい。また、私の質問に対する重要な編集に感謝します。このフォーラムに参加することで、アルカトラズから逃れるという同じスリルを感じることがあります。間違った動きはあなたの人生を意味します。とにかく私は本当にこのコメントは削除される前に、私のおかげであなたを達するたい
クラウディオ・

回答:


25

これは非常に曖昧なコーナーケースであり、テスト[組み込みの定義方法のバグを考慮する場合があります。ただし、[多くのシステムで使用可能な実際のバイナリの動作と一致します。私の知る限り、それだけで特定のケースと一致する値持つ変数に影響する[ようなオペレータ(!=-e、などを。

BashとPOSIXシェルでその理由と回避方法を説明します。


説明:

以下を考慮してください。

x="("
[ "$x" = "(" ] && echo yes || echo no

問題ない; 上記はエラーを生成せず、を出力しますyes。これは、私たちが何かを機能させることを期待する方法です。'1'必要に応じて比較文字列との値を変更するとx、期待どおりに機能します。

実際の/usr/bin/[バイナリは同じように動作することに注意してください。たとえば、実行すると'/usr/bin/[' '(' = '(' ']'エラーは発生しません。これは、引数が単一の文字列比較操作で構成されていることをプログラムが検出できるためです。

ときに我々はバグが発生すると、第二の発現と。有効である限り、2番目の式が何であるかは関係ありません。例えば、

[ '1' = '1' ] && echo yes || echo no

出力yes、および明らかに有効な式です。しかし、2つを組み合わせると、

[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no

バッシュは、以下の場合に式を拒否した場合にのみxです(!

実際の[プログラムを使用して上記を実行する場合、すなわち

'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no

エラーが理解できるであろう:シェルは変数置換を行うため、/usr/bin/[バイナリのみパラメータを受信( = ( -a 1 = 1し、終端が]そこにいる、オープン括弧サブ表現を開始するかどうか、それは当然のことながら解析に失敗したかどうか 関係する動作を制御します。確かに、2つの文字列比較として解析することは可能ですが、そのように貪欲に行うと、括弧で囲まれた部分式を持つ適切な式に適用すると問題が発生する可能性があります。

問題は、実際には、[組み込みのシェルxが式を調べる前に値を拡張した場合と同じように動作することです。

(これらのあいまいさ、および変数展開に関連する他の理由は、Bashが実装された大きな理由であり、[[ ... ]]代わりにテスト式の使用を推奨しています。)


回避策は簡単で、古いshシェルを使用するスクリプトでよく見られます。多くの場合x、文字列の前に「安全な」文字を追加し(両方の値が比較される)、式が文字列比較として認識されるようにします。

[ "x$x" = "x(" -a "x$y" = "x1" ]

1
[組み込みの動作をバグとは呼びません。どちらかといえば、それは固有の設計上の欠陥です。 [[は単なる組み込みコマンドではなくシェルキーワードであるため、引用削除の前に物事を見て、実際の通常の単語分割を実際にオーバーライドします。たとえば、コンテキストは通常​​とは異なるため、[[ $x == 1 ]]を使用する必要はありません。とにかく、これはの落とし穴を回避する方法です。POSIXは、そのように振る舞う必要があり、bashはほとんどなくてもPOSIXに準拠しているため、キーワードに変更しても魅力的ではありません。"$x"[[[[[[--posix[
ピーターコーデス

POSIXでは回避策を推奨していません。を2回呼び出すだけ[です。
ワイルドカード

@PeterCordes:まったく正しい。いい視点ね。(おそらく、最初の段落で定義れた代わりに設計を使用する必要がありましたが、POSIXよりも前の履歴によって負荷がかかっているため、代わりに後者の単語を選択しました。)私は、サンプルスクリプトでの使用を個人的に避けています。私がいつも引用している引用推奨の例外(引用を省略することがスクリプトのバグの最も一般的な理由であるため)、引用の推奨を行わずに、なぜ規則の例外であるのかを説明する簡単な段落を考えていません容疑者。[[[[[
公称動物

@Wildcard:いいえ。POSIXはこの慣行反対することを推奨しておら、それが重要です。当局がたまたまこの慣行を推奨しているわけではないからといって、これを悪くするわけではありません。確かに、私が指摘するように、これはshスクリプトで使用される歴史的な慣行であり、POSIX標準化よりも先行しています。括弧内の部分式と使用-a-oテストで論理演算子を/ [(介してチェーン式に依存することがより効率的である&&||)。現在のマシンでは、違いは関係ありません。
公称動物

@Wildcard:ただし、個人的には&&and を使用してテスト式を連鎖することを好みます||が、その理由は、バグを導入することなく、人間がそれらを維持する(読み、理解し、必要に応じて変更する)ことを簡単にするためです。したがって、私はあなたの提案を批判しているのではなく、提案の背後にある理由だけを批判しています。(非常に類似した理由から、.LT..GE.FORTRAN 77等の比較演算子は、より人間に優しいバージョン得<>=それ以降のバージョンでは、等。)
公称動物

11

[別名test見る:

 argc: 1 2 3 4  5 6 7 8
 argv: ( = 1 -a 1 = 1 ]

test括弧内の部分式を受け入れます。そのため、左括弧が部分式を開き、それを解析しようとしていると考えます。パーサーは=部分式の最初のものと見なし、暗黙の文字列長テストであると考えているため、満足しています。部分式の後に右括弧が続く必要があり、代わりにパーサーがの1代わりに検索します)。そして文句を言います。

場合はtest、正確に3つの引数を持ち、中央の引数は認識事業者の一つであり、それは括弧内の部分式を探していなくて第一と第三引数にその演算子を適用します。

詳細についてはman bash、を検索してくださいtest expr

結論:で使用される解析アルゴリズムtestは複雑です。だけの簡単な表現を使用し、使用したシェルの演算子を!&&そして||それらを結合します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.