不相等的语法重要吗?


22

在编写脚本时,我通常使用以下语法编写ifs,因为我很容易理解接下来发生的事情是不正确的。

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

其他人说下面的方法更好

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

问题是当我问为什么以及是否存在差异时,没有人似乎没有答案。

那么,这两种语法之间有什么区别吗?其中一个比另一个安全吗?还是仅仅是偏好/习惯的问题?


9
在第一种情况下,你需要知道运营商的优先级来区分!(x==y)(!x)==y
jimmij

@jimmij您的意思是说,当比较一个字符串时,它if [ ! "$string" = "one" ]转换为$stringequals 值one。并且这if [ "$string" != "one"]转化为如果的值$string不等于one
Jimmy_A

5
@Jimmy_A我的意思是你需要知道它是如何“翻译”的。将运算符聚集在一起(!=语法)更加明显。
jimmij

亲爱的亲密投票者,斯特凡的答案表明,行为上存在实质性差异,正确答案绝非基于观点的。
凯文(Kevin)

Answers:


35

除了修饰/偏好参数之外,原因之一可能是[ ! "$a" = "$b" ]与相比,在极端情况下失败的实现更多[ "$a" != "$b" ]

如果实现遵循POSIX算法,则两种情况都应该是安全的,但是即使在今天(截至撰写本文的2018年初),仍然有一些实现失败的情况。例如,使用a='(' b=')'

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

对于dash0.5.9之前的版本,例如sh在Ubuntu 16.04上找到的0.5.8 :

$ 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

这些实现被[ ! "(" = ")" ]视为 [ ! "(" "text" ")" ][ ! "text" ](测试“文本”是否为空字符串),而POSIX则将其视为[ ! "x" = "y" ](测试“ x”和“ y”是否相等)。这些实现失败,因为在这种情况下它们执行了错误的测试。

请注意,还有另一种形式:

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

那需要一个POSIX外壳(不适用于旧的Bourne外壳)。

请注意,几个实现也存在[ "$a" = "$b" ](和[ "$a" != "$b" ])问题,并且仍然像Solaris 10上的[内置功能一样/bin/sh(Bourne shell,POSIX shell在中/usr/xpg4/bin/sh)。这就是为什么您看到类似以下内容的原因:

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

在试图移植到旧系统的脚本中。


因此,换句话说,两种语法都一样,没有任何区别,但是! "$a" = "b"您需要更加谨慎地编写它以指定比较。从您的示例中可以理解,第二条命令返回not zero可能是有益的,也可能是麻烦的。如果它们不匹配,则退出,或者如果您想查看比较是否崩溃,这可能会很有益
Jimmy_A

2
@Jimmy_A,不,在这些实现中[ ! "$a" = "$b" ],当$ais ($bis 时会失败),它声称当它们(不相同时它们是相同的,与字符串不同),但是[在那种情况下执行错误的测试。
斯特凡Chazelas

嗯,我想我明白了。第一个和第三个表示检查条件是否为真,而第二和第四个表示检查条件是否为假。因此,出口状态代码不同。基本上来说,1和4变量不同而2和3不相等。
Jimmy_A

3
@Jimmy_A,不。您完全错过了重点。 如果这些实现遵循POSIX,那么所有的状态码将为0
通配符

为了确保我理解,这可以归结为:[ ! "$a" = "$b" ]有时在错误的Shell实施中处理不正确(我认为或多或少都在重述答案的第一句话)。
迈克尔·伯

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

在许多其他语言中也可以观察到类似的行为,包括例如awkUnix世界中经常使用的工具。


另一方面,通过聚集运算符x != y不会导致任何混淆,因为这是一种公认​​的模式。而且,从技术上讲 !=,通常不是两个,而是一个运算符,因此,比起单独比较然后进行求反,评价甚至应稍快一些。因此,尽管两种语法都可以在bash中工作,但我还是建议您遵循,x != y因为这样可以更轻松地阅读和维护遵循某些标准逻辑的代码。


4

这种事情是非常基于意见的,因为“答案”在很大程度上取决于个人大脑碰巧连接的方式。虽然在语义上NOT ( A == B )确实与相同(A != B ),但一个人可能更清楚,另一个人可能更清楚。它也取决于上下文。例如,如果我设置了一个标志,则一种语法比另一种语法的含义可能更清楚:

if NOT ( fileHandleStatus == FS_OPEN )

相对于

if ( fileHandleStatus != FS_OPEN )

甚至if ( FS_OPEN != fileHandleStatus )是由于容易意外键入=而不是==使用前者是赋值语言而后一种相等测试(例如C)的语言的结果……
derobert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.