在shell编程中出现了意外的操作符。

240

我的代码:

    #!/bin/sh
    #filename:choose.sh
    read choose
    [ "$choose" == "y" -o "$choose" == "Y" ] && echo "Yes" && exit 0
    [ "$choose" == "n" -o "$choose" == "N" ] && echo "No"  && exit 0
    echo "Wrong Input" && exit 0

但是当我执行

    sh ./choose.sh

终端提示我说

   [: 4: n: :Unexpected operator
   [: 5: n: :Unexpected operator

我的bash脚本中有错误吗? 谢谢!


当我在Linux和cygwin中执行相同的代码时,没有出现任何错误。 - Raghuram
2
Cygwin很可能将sh别名为bash。一些发行版不再提供真正的sh。尽管有人会争论(我也倾向于同意),如果您编写一个要可移植的脚本,请使用sh而不是bash - Wolph
我的问题是我需要执行 source foobar.sh 而不是 ./foobar.sh - jsta
5
两个错误:1. 在/bin/sh中应该使用“=”而不是“==”;2. 不能处理空字符串。使用 ${choose}BLAH == yBLAH 来修复这个问题。因此,技术上讲这不是一个重复的问题。 - personal_cloud
@personal_cloud,请不要推荐${choose}BLAH的方法--最好只是引用,使用"$choose";自1970年以来就不再需要常量前缀/后缀(只要避免使用当前POSIX“test”标准中标记为过时的功能,如-a-o)。 - Charles Duffy
在我的情况下,变量是空的。 - Tsagana Nokhaeva
7个回答

404

你的 bash 脚本没有错误。但是你正在使用语法较少的 sh 执行它。

因此,你需要运行 bash ./choose.sh,或将脚本转换为仅使用符合 POSIX 标准的 sh 命令,例如字符串之间使用 = 而不是 ==


5
bash 语法是 sh 语法的超集 - 您系统上的 /bin/sh 可执行文件可能仅提供标准的 sh 功能,其中不包括 [] 形式的测试。 - Tim
14
是的。它们是完全不同的shell。尽管bash基于并且在很大程度上向后兼容sh,实际上它们可能是你系统中的同一个程序,但是根据使用的名称,它们仍然会表现出不同的行为。你可以通过将脚本的第一行改为“#!/bin/bash”并使文件可执行来自动使用bash运行脚本,然后只需运行“./choose.sh”。 - Tyler McHenry
16
这个答案部分错误。完全正确的是由Nietzche-jou投票评为最佳的答案。sh支持[==适用于Bash内建的[[test[,而POSIX版本的[test要求单个=``。 (顺便说一下,Bash手册也是这么说的。) 因此,因为这个原因而要求使用Bash是对可移植性的无谓损害。命令名称是[test]`只是不必要的、未使用的最后一个参数。很多shell将它们实现为内建命令,正如维基百科所说的那样。 - Palec
1
@Palec:虽然这个答案不如Nietzche-jou的答案详细,但我不明白有什么问题?我从来没有提到过括号:) 无论如何,你的评论值得一加!+1 - Wolph
4
@Wolph 说“部分错误”,我指的是两件事。首先,问题不在于 Bash vs sh 语法,两者都可以正确调用 [ 命令。问题在于 [ 命令参数的语法,这不是 sh 的业务范围。其次,对于这样的工作,Bash 太过复杂了,尤其是当存在更简单的解决方案时。虽然这并不完全错误,因为它也解决了问题,但我认为这是一个普遍的坏建议。这会让初学者得出错误的结论,即 Bash 可以解决他们的问题。它有许多不可移植的扩展,超出了 POSIX 所需的功能。我相信我们应该引导初学者编写可移植的程序。 - Palec
显示剩余10条评论

389

POSIX标准的sh不能理解字符串等于操作符==,因为这是bash的一个特性。请使用=代替。

顺便说一下,其他声称sh不支持括号的人是错误的。


11
是的,我试着只用"="替换"==",脚本也可以通过 "sh ./choose.sh"来执行。Bash和Sh都支持括号。 - kit.yang
73
在我看来,这个答案比不必要地使用bash更好。 - John Kugelman
3
在我的情况下,我的Teamcity代理默认运行sh命令,我不想改变这一点(因为这会导致其他问题)。在这里需要使用SH,我认为这是必须使用sh命令的正确答案。 - Nicolas Oliver
+1 这是正确的答案。任何想要为sh解决这个问题的人都应该只使用单个'='。仅阅读Wolph的最佳回复浪费了我3个小时:( - Umer
2
仍然是非常有价值的提示。许多Docker镜像只有sh而没有bash。 - Steve

9
要在 Bash 中执行它,请使用 #!/bin/bash 并将其设置为可执行文件,然后使用。
./choose.sh

6

您可以使用case/esac代替if/else语句

case "$choose" in
  [yY]) echo "Yes" && exit;;
  [nN]) echo "No" && exit;;
  * ) echo "wrong input" && exit;;
esac

5

实际上,"["方括号只是test命令的一个内部shell别名。

因此你可以这样写:

test -f "/bin/bash" && echo "This system has a bash shell"

或者

[ -f "/bin/bash" ] && echo "This system has a bash shell"

无论是在sh还是bash中,它们的等价性是相同的。请注意,在“[”命令上需要有一个闭合的“]”括号,但除此之外,“[”与“test”相同。“man test”是一个好东西。


man test 是 /bin/test 的 man 手册页,而非内置的 Shell 函数。 - stew
ls $(which [) gives /usr/bin/[ - vp_arth
对我没用。 - ttfreeman

5

你需要使用bash,或者重写你的脚本使用标准的sh。

sh -c 'test "$choose" = "y" -o "$choose" = "Y"'

我认为使用“[ ]”比使用“test”更方便。 似乎“bash ./choose.sh”可以解决问题。 - kit.yang
@kit 无论如何,sh更加轻便易携带。 - Anycorn
3
括号有什么方便之处?由于人们没有意识到"[ ]"是"test"的同义词,以及在括号周围省略空格而导致的持续错误,这些历史上的混淆很难弥补单个字符节省的效果。(例如:"if [ $x = 5 ]"与"if test $x = 5"相比,前者为13个字符,后者为14个字符。) - William Pursell

-6

不要使用任何保留关键字作为任何变量名称的开头: 例如,HOSTNAME将失败,因为HOST {TYPE | NAME}是保留的


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接