等しくないという構文は重要ですか?


22

スクリプトを作成するとき、次に来ることは真実ではないことを理解しやすいため、通常は次の構文でifを記述します。

if [ ! "$1" = "$2" ]; then

他の人は、以下の方法が良いと言います

if [ "$1" != "$2" ]; then

問題は、理由と違いがあるかどうかを尋ねたとき、誰も答えていないようです。

では、2つの構文に違いはありますか?それらの1つは他より安全ですか?それとも単に好み/習慣の問題ですか?


9
最初のケースでは、オペレータに区別するために優先順位を知る必要がある!(x==y)からに(!x)==y
-jimmij

あなたは、文字列を比較するときは、このことを意味しています@jimmij if [ ! "$string" = "one" ]ない場合の値に変換$stringイコールone。そして、これif [ "$string" != "one"]はの値が?$stringと等しくない場合に変換されoneます
Jimmy_A

5
@Jimmy_Aこれがどのように「翻訳する」かを知る必要があるということです。演算子を一緒に収集(!=構文)することは、より明白です。
-jimmij

親愛なる投票者の皆さん、ステファンの答えは、行動に重大な違いがあることを示しています。正しい答えは意見に基づくものではありません。
ケビン

回答:


35

外観/設定引数のほかに、理由の1つ[ ! "$a" = "$b" ]は、の場合よりもコーナーケースで失敗する実装が多いことです[ "$a" != "$b" ]

実装がPOSIXアルゴリズム準拠していれば、どちらのケースも安全であるはずですが、今日(執筆時点で2018年初頭)でも、失敗する実装が残っています。たとえば、とa='(' b=')'

$ (a='(' b=')'; busybox test "$a" != "$b"; echo "$?")
0
$ (a='(' b=')'; busybox test ! "$a" = "$b"; echo "$?")
1

dash発見0.5.8のようなより前のバージョン0.5.9に、sh例えばUbuntuの16.04で:

$ a='(' b=')' dash -c '[ "$a" != "$b" ]; echo "$?"'
0
$ a='(' b=')' dash -c '[ ! "$a" = "$b" ]; echo "$?"'
1

(0.5.9で修正済み、https: //www.mail-archive.com/dash@vger.kernel.org/msg00911.htmlを参照)

これらの実装は[ ! "(" = ")" ]そのまま (「テキスト」がヌル文字列かどうかをテストします)POSIXがそれを要求します(「x」と「y」が等しいかどうかをテストします)。それらの実装は、その場合に間違ったテストを実行するため失敗します。[ ! "(" "text" ")" ][ ! "text" ][ ! "x" = "y" ]

さらに別のフォームがあることに注意してください。

! [ "$a" = "$b" ]

そのためにはPOSIXシェルが必要です(古いBourneシェルでは機能しません)。

いくつかの実装では[ "$a" = "$b" ](および[ "$a" != "$b" ])にも問題が[あり/bin/sh、Solaris 10の組み込み(Bourneシェル、POSIXシェルは/usr/xpg4/bin/sh)のようになっていることに注意してください。そのため、次のようなものが表示されます。

[ "x$a" != "x$b" ]

古いシステムに移植しようとするスクリプト内。


つまり、両方の構文は同じであり、違いはまったくありませんが! "$a" = "b"、比較を指定するためにどのように記述するかについてより注意する必要があります。あなたの例から、私は2番目のコマンドが返さnot zeroれることを理解しています。一致しない場合に終了する場合、または比較がクラッシュしたかどうかを確認する場合に問題が発生する場合は有益な場合があります
-Jimmy_A

2
@Jimmy_Aが、いや、それらの実装に[ ! "$a" = "$b" ]するときだけ失敗$aである($bされ)、それは彼らがいないときそれらが同一であると主張し、(同じ文字列ではありません)が、[その場合には、間違ったテストを実行します。
ステファンシャゼラス

ああ、私はそれを得たと思う。1番目と3番目は条件が真かどうかをチェックし、2番目と4番目は条件が偽かどうかをチェックします。したがって、終了ステータスコードは異なります。基本的に、1と4の変数は異なり、2と3は等しくありません。
Jimmy_A

3
@Jimmy_A、いいえ。あなたはそのポイントを完全に逃しました。 場合は、これらの実装は、POSIXに続く、その後、すべてのステータスコードは0になります
ワイルドカード

私が理解したことを確認するために、これは次のように[ ! "$a" = "$b" ]要約します:バグのあるシェルの実装では時々誤って処理されます(答えの最初の文を多かれ少なかれ言い直していると思います)。
マイケルバー

8

エラーが発生しやすいx != yため、構文は優れ! x == yています。言語ごとに異なる演算子の優先順位に関する知識が必要です。vsの優先度に応じて、構文! x == y!(x == y)またはとして解釈できます。(!x) == y!=


たとえば、c++否定! は比較/関係演算子の前に来る ==ため、次のコードになります。

#include<iostream>

using namespace std;

int main()
{
  int x=1, y=2;
  if(     x  != y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(  !  x  == y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(  !( x  == y ) ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(   (!x) == y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
}

返す

true
false
true
false

同様の動作は、他の多くの言語でも観察できます。たとえばawk、Unixの世界で頻繁に使用されるツールです。


一方、演算子を経由x != yして収集しても、十分に確立されたパターンとして混乱することはありません。さらに、技術的に言えば !=2つではなく1つの演算子であることが非常に多いため、比較と否定を別々に行うよりも、評価するのがわずかに速いはずです。したがって、両方の構文はbash x != yで機能しますが、標準ロジックに従うコードを読み、保守する方が簡単なので、従うことをお勧めします。


4

「答え」は個人の脳の配線方法に非常に強く依存するため、この種のことは非常に意見に基づいています。意味的にがとNOT ( A == B )同じであることは事実ですが(A != B )、一方はある人にとってより明確であり、もう一方は別の人にとってより明確かもしれません。また、コンテキスト依存です。たとえば、フラグが設定されている場合、構文は別の構文よりも明確になります。

if NOT ( fileHandleStatus == FS_OPEN )

とは対照的に

if ( fileHandleStatus != FS_OPEN )

とにもif ( FS_OPEN != fileHandleStatus )誤って入力のしやすさの結果として=の代わりに、==前者が割り当て及び(Cなど)後で平等テストである言語で...
derobert
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.