not equal 的语法重要吗?

Jim*_*y_A 24 shell test

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

if [ ! "$1" = "$2" ]; then
Run Code Online (Sandbox Code Playgroud)

还有人说下面的方法更好

if [ "$1" != "$2" ]; then
Run Code Online (Sandbox Code Playgroud)

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

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

Sté*_*las 37

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

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

$ (a='(' b=')'; busybox test "$a" != "$b"; echo "$?")
0
$ (a='(' b=')'; busybox test ! "$a" = "$b"; echo "$?")
1
Run Code Online (Sandbox Code Playgroud)

对于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
Run Code Online (Sandbox Code Playgroud)

(在 0.5.9 中修复,参见https://www.mail-archive.com/dash@vger.kernel.org/msg00911.html

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

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

! [ "$a" = "$b" ]
Run Code Online (Sandbox Code Playgroud)

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

请注意,一些实现在[ "$a" = "$b" ](和[ "$a" != "$b" ])方面也有问题,并且仍然像Solaris 10 上的[内置程序一样/bin/sh(Bourne shell,POSIX shell 在 中/usr/xpg4/bin/sh)。这就是为什么您会看到以下内容:

[ "x$a" != "x$b" ]
Run Code Online (Sandbox Code Playgroud)

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

  • @Jimmy_A,不。你完全没有抓住重点。*如果*这些实现遵循POSIX,那么*所有*状态代码将为0。 (3认同)
  • @Jimmy_A,不,在这些实现中`[ ! "$a" = "$b" ]` 只是在 `$a` 是 `(` 并且 `$b` 是 `)` 时失败,当它们不同时,它声称它们是相同的,`(` 是不一样的string 为 `)`,但 `[` 在这种情况下执行错误的测试。 (2认同)

jim*_*mij 8

x != y语法是更好,因为! x == y很容易出错-需要运营商优先的知识,不同从语言到语言。语法! x == y可以解释为!(x == y)(!x) == y,具体取决于!vs 的优先级=


例如,在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;
}
Run Code Online (Sandbox Code Playgroud)

返回

true
false
true
false
Run Code Online (Sandbox Code Playgroud)

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


另一方面,将操作符聚集在一起x != y并不会导致任何混淆,因为这是一种既定的模式。此外,从技术上讲 !=,通常不是两个,而只是一个运算符,因此比单独比较然后否定的评估速度甚至要快一点。因此,虽然这两种语法都在 bash 中工作,但我建议遵循,x != y因为它更容易阅读和维护遵循一些标准逻辑的代码。