在 if 语句中使用重定向或写入文件如何以及为何会影响退出代码?

在 if 语句中使用重定向或写入文件如何以及为何会影响退出代码?

桀骜

echo 'a string' > test.txt
echo $?
0

[[ $(echo 'a string') ]]
echo $?
0

然而

[[ $(echo 'a string' > test.txt) ]]
echo $?
1

另一个例子

curl -so 'curl-8.2.1.tar.gz' https://curl.se/download/curl-8.2.1.tar.gz
echo $?
0

或者

[[ $(curl -so 'curl-8.2.1.tar.gz' https://curl.se/download/curl-8.2.1.tar.gz) ]]
echo $?
1

我的问题:

  1. 这是因为输出重定向吗?如果不是,是什么原因造成的?
  2. 命令执行成功:a string出现在test.txt并且curl将文件下载到我指定的输出文件中,为什么评估结果为false
  3. 在脚本中是否有一种合理的方法来处理这个问题?假设我想执行某个命令,如果前一个命令执行成功(但false仍然返回),应该如何执行?我可以添加第二次检查来查看该行是否出现或文件是否已下载,但首先就不需要评估命令是否成功执行。

更多完整性示例(可读性与“正确性”?):

if ( $(echo 'a string' > text.txt) ); then echo yes; else echo no;fi
yes
if (( $(echo 'a string' > text.txt) )); then echo yes; else echo no;fi
no
if $(echo 'a string' > text.txt); then echo yes; else echo no;fi
yes
if echo 'a string' > text.txt; then echo yes; else echo no;fi
yes

答案1

$?

在里面由外壳设置的参数man 的部分zshparam,或者info zsh 'Parameters Set By The Shell' 你会看到,$?是最后一个命令返回的退出状态。

$(...)

在人身上zshexpn,或者info zsh 'Command Substitution'你会看到:

命令替换

'$(...)'括在括号中且前面带有美元符号(如)或用重音符号引用(如)的命令 ...将被替换为其标准输出,并删除所有尾随换行符。

例如,表达式$(echo 'a string')将被替换为该命令 ( ) 的输出,a string<newline>并删除尾随换行符,从而得到a string.但里面的命令$(echo 'a string' > test.txt)不会向标准输出产生任何输出,因此该表达式的结果是一个空字符串。

[[ ... ]]

zshmisc在或的手册页中info zsh 'Conditional Expressions',您将看到:

条件表达式

条件表达式与复合命令一起使用[[来测试文件的属性并比较字符串。每个表达式都可以由以下一个或多个一元或二元表达式构造:

[...]

为了兼容性,如果有一个在语法上不重要的参数(通常是变量),则条件被视为 测试表达式是否扩展为非零长度的字符串。

现在举个例子

因此,在下一行中, 中命令的输出$( ... )是字符串a string<newline>,因此其自身的扩展$(...)将是a string,这是一个非零长度的字符串,因此[[ ... ]]计算将返回 true(0 退出状态)。

[[ $(echo 'a string') ]]

但在下面的命令中,由于您进行了重定向,因此 的扩展$( ... )将是零长度字符串,并且[[ ... ]]计算将返回 false(退出状态 1,是 0 以外的数字)。

[[ $(echo 'a string' > test.txt) ]]

同样,下面命令的退出状态就是该curl命令的退出状态。

curl -so 'curl-8.2.1.tar.gz' https://curl.se/download/curl-8.2.1.tar.gz

will的联机帮助页curl说明了在什么条件下它返回非零(失败)退出状态。

但在下一个示例中,退出状态将取决于curl命令是否有任何输出(成功)或没有任何输出(失败)。

[[ $(curl -so 'curl-8.2.1.tar.gz' https://curl.se/download/curl-8.2.1.tar.gz) ]]

(( ... ))我就到这里为止,但是你可以继续阅读手册,例如算术评估人的一部分zshmisc(或info zsh 'Arithmetic Evaluation')

答案2

我由此得出结论, using[[ ... ]]不适合评估成功的命令执行,除非以命令的输出结果产生非零字符串的方式理解成功的命令执行。为了在 zsh 脚本中进行验证,正确的方法是

  1. if command; then ...; [else ...;] fi(按照 muru 的评论所述使用)
  2. 单独运行该命令并$?随后检查其退出代码,和/或
  3. 验证命令的实际输出,例如通过检查它生成的文件的内容

相关内容