默认测试表达式在zsh vs bash中表现不同 - 为什么?

时间:2016-09-21 03:21:22

标签: bash zsh

这是一个简单的测试用例脚本,当我从命令行运行$ source test_script.sh时,它在zsh vs bash中的行为有所不同。我不一定知道为什么我的shebang明确表示我希望bash运行我的脚本而不是which命令是zsh中的内置和bash中的程序这一事实。 (仅供参考 - shebang目录是我的bash程序所在的地方,可能与你的不一样 - 我使用自制程序安装了新版本)

#!/usr/local/bin/bash
if [ "$(which ls)" ]; then
    echo "ls command found"
else 
    echo "ls command not found"
fi

if [ "$(which foo)" ]; then
    echo "foo command found"
else
    echo "foo command not found"

我使用zsh和Bash中的source ./test-script.sh运行此脚本。

zsh输出:

ls command found
foo command found

以bash输出:

ls command found
foo command not found

我的理解是test[ ]的默认值(它们是相同的)如果字符串不为空/ null,则将其评估为true。举例说明:

的zsh:

$ which foo
foo not found

击:

$ which foo

$

此外,如果我在zsh中重定向标准错误,如:

$ which foo 2> /dev/null
foo not found

zsh似乎仍然将foo not found发送到标准输出,这就是为什么(我猜)我在zshell下传递的测试用例;因为"$(which xxx)"的扩展在两种情况下都返回了一个字符串(例如/some/directoryfoo not found(zsh总是会返回一个字符串?)。

最后,如果我删除双引号(例如$(which xxx)),zsh会给我一个错误。这是输出:

ls command found
test_scritp.sh:27: condition expected not:

我猜zsh希望我使用[ ! "$(which xxx)" ]。我不明白为什么?它在bash中运行时从未出现过错误(并且不管怎么说这不应该在bash中运行?!)。

为什么我的脚本不使用bash?为什么这样的事情如此微不足道?我理解如何使用-e选项使其工作正常,但我只是想了解为什么会发生这种情况。它让我疯狂。

1 个答案:

答案 0 :(得分:5)

这里有两个不同的问题。

首先,使用的正确命令是type,而不是which。就像你注意到的那样,命令whichzsh内置的,而在Bash中,它将执行你系统上发生的任何which命令。有许多具有不同行为的变体,这就是为什么POSIX选择引入替换而不是试图为which规定特定行为 - 然后还会有一个可能的行为,并且无法轻易地根除所有其他遗留行为。 (一个早期的常见问题是使用which命令检查csh环境,即使您实际使用了不同的shell。)

其次,检查命令的字符串输出是一个严重的反模式,因为字符串(“未找到”与“nicht gefunden”与“eilöytynyt”与等等)和程序版本之间的字符串不同 - 正确的解决方案是检查命令的退出代码。

if type ls >/dev/null 2>&1; then
    echo "ls command found"
else
    echo "ls command not found"
fi

if type foo >/dev/null 2>&1; then
    echo "foo command found"
else
    echo "foo command not found"
fi

(一个相关的反模式是明确地检查$?。很少有任何需要这样做,因为它是由shell的流控制语句自然而透明地完成的,如if和{{ 1}}。)

关于引用,shell对未加引号的值执行空白标记化和通配符扩展,因此如果while$string,则表达式为

command not found

值没有引号,评估为

[ $string ]

它看起来就像字符串“command”一样,后面跟着一些语法无效的错误。

最后,正如我们在聊天会话中发现的那样(从评论中链接),OP对[ command not found ] 的确切含义感到困惑,并最终在一个单独的进程中运行Bash脚本。 (source代替./test-script)。为了记录,当你source ./test-script一个文件时,你会让当前的shell读取并执行它;在这个设置中,脚本的shebang行只是一个注释,并且被shell完全忽略。