我注意到.sh
自制程序有不同的脚本来检查它是否已安装。
使用这种语法:
if test ! $(which brew); then
另一个使用这种语法:
if ! which brew > /dev/null; then
我知道他们都在检查 的退出代码which brew
,但我很好奇它们之间是否还有其他差异。对我来说,前者更清晰,并且我认为(可能是错误的)更有效,因为它不必重定向任何输出。
另外,为什么第二个实际上执行which brew
,无论输出是否发送到/dev/null
? shell到底是如何工作的?
答案1
第一个是笨拙的黑客,第二个是常见的错误。
这两个测试做了完全不同的事情,但却产生了相同的结果。
if test ! $(which brew); then
这测试是否输出的which brew
为空。
- 如果
brew
位于搜索路径上,则which brew
生成一个输出字,因此该test
命令接收两个参数:!
和 的路径brew
。当test
有两个参数时,第一个参数是!
,如果第二个参数为空(这里不是这种情况),则返回 true ,否则返回 false 。 - 如果
brew
不在搜索路径上,则 的输出which brew
为空,因此该test
命令接收单个参数!
,因此test
返回 true。
请注意,如果 的路径brew
包含空格,此命令将产生错误消息和失败状态,因为这就是不带引号的命令替换的含义。碰巧失败状态是这里期望的结果,所以这是一种迂回的方式。
此命令不测试 的退出代码which brew
。退出代码被忽略。
if ! which brew > /dev/null; then
这是测试是否which brew
成功的直接方法。除了自身之外,它不依赖于任何脆弱的东西which
。
which brew
在这两种情况下总是被调用。为什么输出重定向到 很重要/dev/null
? “隐藏此命令的输出”并不意味着“不运行此命令”。
brew
测试命令搜索路径上是否不可用的正确方法是
if ! type brew >/dev/null 2>/dev/null; then
答案2
第一部分if test ! $(which brew); then ...
- 如果您运行,则会搜索名为 的可执行
which brew
文件。如果找到,则名称将打印到标准输出。如果未找到任何内容,则会将错误消息打印到 stderr。 (某些 shell 提供了一个内置函数,它还报告内置函数和 shell 函数、保留字……例如。)$PATH
brew
which
zsh
$(...)
捕捉到标准输出(但不是 stderr)无论...
shell 命令是什么,并将其作为参数放在命令行上。test ! ...
返回 的反向返回码test ...
。这是test
否定表达式的方式。test ...
(该部分中没有任何选项...
)仅测试该...
部分是否不是空字符串。
结果:which
用于查找名为 的可执行文件brew
。生成的路径或空字符串作为参数放在命令行上,test !
检查该参数是否为空字符串,然后返回0
(true)。
第二部分if ! which brew > /dev/null; then ...
这里which brew
和上面的一样。如果which
找到可执行文件,它不仅会打印名称,还会返回代码0
(true)。如果没有找到任何内容,它会打印一条错误消息并返回一些其他代码(错误)。由于!
命令前面的 ( 0
-> 1
,任何其他 -> 0
),此代码被 shell 反转。这才是if
关心的。因为用户不关心看到brew
标准输出which
被重定向到的实际路径/dev/null
。
笔记:
在这两种情况下,stderr 都which
不会被重定向,并且应该在终端上可见。
我没有测试它,但我认为第二种形式更快,因为它只启动一个进程/内置进程。但这两个命令都会重定向:第二个命令明确地重定向到,第一个命令通过捕获with/dev/null
的输出而不太明显(一开始)。您可以粗略地比较速度,例如which
$(...)
comp
我使用的功能。
有which
和 的手册页test
。 shell 的手册页(sh
、ksh
、bash
、zsh
)有一个关于重定向和命令替换以及内置函数的部分(不仅which
而且test
通常作为内置函数实现)。