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

我真正想做的事

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

解决办法?

经过一番谷歌搜索后,我在StackOverflow 以及AskUbuntuUnix/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 吗?我无法重现。
  • POSIX 要求您的预期行为,因此它应该可以在任何兼容的 shell 中工作。
  • 如果你使用var=$(false); echo $?,你会看到什么?
  • 这似乎是特定于 info 命令的(也许只是它的某些版本?)。在我的快速测试中,如果它的标准输出不是 tty,它会以成功状态退出(尽管实际上失败了)。

标签: linux bash shell unix sh


【解决方案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#L848https://github.com/debian-tex/texinfo/blob/master/info/info.c#L277https://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,所以我正在使用这种行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2017-02-11
    • 2016-10-19
    • 1970-01-01
    • 2021-04-27
    相关资源
    最近更新 更多