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

tom*_*c38 2 bash zsh

这是一个简单的测试用例脚本,当我从命令行运行时,它在zsh vs bash中的行为有所不同$ source test_script.sh.我不一定知道为什么有一个区别,如果我的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"
Run Code Online (Sandbox Code Playgroud)

source ./test-script.sh从zsh和Bash 运行这个脚本.

zsh中的输出:

ls command found
foo command found
Run Code Online (Sandbox Code Playgroud)

bash输出:

ls command found
foo command not found
Run Code Online (Sandbox Code Playgroud)

我的理解是,如果字符串不为空/空,则默认为test[ ](它们是相同的)将字符串计算为true.为了显示:

zsh的:

$ which foo
foo not found
Run Code Online (Sandbox Code Playgroud)

庆典:

$ which foo

$
Run Code Online (Sandbox Code Playgroud)

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

$ which foo 2> /dev/null
foo not found
Run Code Online (Sandbox Code Playgroud)

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

我猜zsh想让我用[ ! "$(which xxx)" ].我不明白为什么?它在bash中运行时从未出现过错误(并且不应该在bash中运行吗?!).

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

tri*_*eee 6

这里有两个不同的问题.

首先,使用的正确命令type不是which.就像你注意到的那样,命令which是一个zsh内置命令,而在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
Run Code Online (Sandbox Code Playgroud)

(A相关的反模式是检查$?明确.有很少任何必要做这个,因为它是由外壳的流量控制语句自然和透明的,但是像ifwhile).

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

[ $string ]
Run Code Online (Sandbox Code Playgroud)

没有引号围绕值评估

[ command not found ]
Run Code Online (Sandbox Code Playgroud)

它看起来像字符串"命令",然后是一些语法无效的字符串.

最后,正如我们在聊天会话中发现的那样(从评论中链接),OP对其确切含义感到困惑source,并最终在一个单独的进程中运行Bash脚本.(./test-script而不是source ./test-script).对于记录,当您source使用文件时,会导致当前shell读取并执行它; 在这个设置中,脚本的shebang行只是一个注释,并且被shell完全忽略.