为啥这个 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 命令返回值会改变?的主要内容,如果未能解决你的问题,请参考以下文章