“测试”和使用 /dev/null 进行评估有什么区别?

“测试”和使用 /dev/null 进行评估有什么区别?

我注意到.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 ...

  1. 如果您运行,则会搜索名为 的可执行which brew文件。如果找到,则名称将打印到标准输出。如果未找到任何内容,则会将错误消息打印到 stderr。 (某些 shell 提供了一个内置函数,它还报告内置函数和 shell 函数、保留字……例如。)$PATHbrewwhichzsh
  2. $(...)捕捉到标准输出(但不是 stderr)无论...shell 命令是什么,并将其作为参数放在命令行上。
  3. 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 的手册页(shkshbashzsh)有一个关于重定向和命令替换以及内置函数的部分(不仅which而且test通常作为内置函数实现)。

相关内容