为啥这个 bash 命令返回值会改变?

Posted

技术标签:

【中文标题】为啥这个 bash 命令返回值会改变?【英文标题】:Why does this bash command's return value change?为什么这个 bash 命令返回值会改变? 【发布时间】:2021-07-02 09:34:37 【问题描述】:

我真正想做的事

保存命令的输出并检查其返回状态。

解决办法?

经过一番谷歌搜索后,我在*** 以及AskUbuntu 和Unix/Linux StackExchange 上找到了基本相同的答案:

if output=$(command); then
  echo "success: $output"
fi

问题

使用command info put 尝试此解决方案时,即使实际命令失败,也会执行 if 子句,但我无法解释为什么?

我尝试手动检查返回值$?,似乎var= 更改了返回值:

$ info put
info: No menu item 'put' in node '(dir)Top'
$ echo $?
1

$ command info put
info: No menu item 'put' in node '(dir)Top'
$ echo $?
1

$ var=$(command info put)
info: No menu item 'put' in node '(dir)Top'
$ echo $?
0

$ var=$(command info put); echo $?
info: No menu item 'put' in node '(dir)Top'
0

`

那么为什么这种通用解决方案在这种情况下不起作用? 以及如何更改/调整解决方案以使其正常工作?

我的环境/系统

我正在使用 WSL2 Ubuntu 20.04.2 LTS 在 Windows 10 上工作:

$ tmux -V
tmux 3.0a

$ echo $SHELL
/bin/bash

$ /bin/bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)

$ info --version
info (GNU texinfo) 6.7

【问题讨论】:

您确定您使用的是 bash 吗?我无法重现。 见***.com/questions/20157938/… POSIX 要求您的预期行为,因此它应该可以在任何兼容的 shell 中工作。 如果你使用var=$(false); echo $?,你会看到什么? 这似乎是特定于 info 命令的(也许只是它的某些版本?)。在我的快速测试中,如果它的标准输出不是 tty,它会以成功状态退出(尽管实际上失败了)。 【参考方案1】:

使用命令信息尝试此解决方案时,即使实际命令失败,也会执行 if 子句,但我无法解释自己为什么?

确实,当输出不是终端并且出现错误时,info 会以 0 退出。

// texinfo/info.c
  if ((!isatty (fileno (stdout))) && (user_output_filename == NULL))
    
      user_output_filename = xstrdup ("-");
   ...
    
  ...
      // in called get_initial_file()
      asprintf (error, _("No menu item '%s' in node '%s'"),
        (*argv)[0], "(dir)Top");
  ...
  if (user_output_filename)
    
      if (error)
        info_error ("%s", error);
      ...
      exit (0);                    // exits with 0!
    

参考:https://github.com/debian-tex/texinfo/blob/master/info/info.c#L848、https://github.com/debian-tex/texinfo/blob/master/info/info.c#L277、https://github.com/debian-tex/texinfo/blob/master/info/info.c#L1066。

为什么这种通用解决方案在这种情况下不起作用?

因为当命令的输出不重定向到终端时,命令的行为会发生变化。

如何更改/调整解决方案以使其正常工作?

你可以模拟一个 tty - https://unix.stackexchange.com/questions/157458/make-program-in-a-pipe-think-it-has-tty , https://unix.stackexchange.com/questions/249723/how-to-trick-a-command-into-thinking-its-output-is-going-to-a-terminal 。

您可以获取命令的stderr 并检查它是否为非空或与某些正则表达式匹配。

我想你也可以联系 texinfo 开发人员,让他们知道我认为这是一个错误并制作补丁,所以它就像exit(error ? EXIT_FAILURE : EXIT_SUCCESS);

【讨论】:

所以这是info 中的错误?我们应该创建一个 PR 来解决这个问题吗? 在我的bashrc 中,我有一个名为my_less 的函数,它检查命令是否成功退出,如果成功则将其输出传送到less(基本上是我的问题中的“解决方案”代码) .所以我现在应该改变那个函数,让它每次和每次调用都模拟一个 tty,只是因为 info 没有按预期工作? 对我来说,它看起来像一个错误。就是info -o /root/somefile get 成功退出,而命令失败。 I should now change... 我只会专门处理info,特别是解决方法,如if [[ "$1" == "info" ]]; then handle info specially,会发生错误。然后当 bug 修复后,你会 if [[ "$1" == "info" ]] && info_version lower_then something; then handle info specially.【参考方案2】:

我没有检查命令的退出状态,而是保存了输出并简单地检查是否有任何输出可用于进一步处理(在我的情况下,管道进入 less):

my_less () 
  output=$("$@")
  if [[ ! -z "$output" ]]; then
    printf '%s' "$output" | less
  fi

即使有info 中的错误,我的函数现在也可以工作,因为错误只会影响命令的退出状态。它的错误消息按预期写入stderr,所以我正在使用这种行为。

【讨论】:

以上是关于为啥这个 bash 命令返回值会改变?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的数组值会发生变化? [复制]

为啥python脚本返回的值会被shell修改?

返回值会发生啥?

bash命令行返回值和展开

为啥这个函数会返回这个值? [复制]

为啥使用 Bash 只读变量捕获输出无法捕获返回码 $?