这是一个简单的测试用例脚本,当我从命令行运行$ 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/directory
和foo not found
(zsh总是会返回一个字符串?)。
最后,如果我删除双引号(例如$(which xxx)
),zsh会给我一个错误。这是输出:
ls command found
test_scritp.sh:27: condition expected not:
我猜zsh希望我使用[ ! "$(which xxx)" ]
。我不明白为什么?它在bash中运行时从未出现过错误(并且不管怎么说这不应该在bash中运行?!)。
为什么我的脚本不使用bash?为什么这样的事情如此微不足道?我理解如何使用-e
选项使其工作正常,但我只是想了解为什么会发生这种情况。它让我疯狂。
答案 0 :(得分:5)
这里有两个不同的问题。
首先,使用的正确命令是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
(一个相关的反模式是明确地检查$?
。很少有任何需要这样做,因为它是由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完全忽略。