如何解决No-op shell命令保留$?
仅使用POSIX shell的功能,是否有一个不执行任何操作且不会更改$?
的值的“简单命令” 人们通常将:
描述为shell的无操作命令,但这总是将$?
设置为零,所以这不是我想要的。
生成外壳程序脚本的程序需要此功能。在几个地方,它需要发出一个if-then-else块
if CONDITION
then
TRUE-COMMANDS
else
FALSE-COMMANDS
fi
但是,由于我不想立即解释的更多复杂性,因此无法可靠地判断TRUE-COMMANDS
和FALSE-COMMANDS
是否为空。空的then或else子句将是shell语法错误。
:
放在then
子句的开头,以照顾TRUE-COMMANDS
为空,但这不适用于FALSE-COMMANDS
,因为它会阻塞$?
和FALSE-COMMANDS
可能要查看条件设置的值$?
。由于相同的原因,:
不能放在之后 FALSE-COMMANDS
— if-then-else之后的代码可能希望从最后看在if-then-else中进行操作。
奖励积分,如果可以避免的话:
-
派生:
$?
可以完成任务,但是在生成的脚本中有很多这样的条件块,因此产生了可衡量的速度下降。 -
功能:先给定
(exit $?)
,然后nop () { return $? }
才能完成工作,但是由于我宁愿不参加的更多复杂性,拥有它也不现实nop
为所有需要它的地方定义得足够早。
解决方法
在兼容POSIX的外壳中肯定不可能编写不触碰$?
的命令。通过阅读IEEE Std 1003.1-2017 vol。 3第2章“ Shell命令语言”,我们了解到:
- 第2.8.2节说“ [e]每个命令都具有退出状态”(因此,没有命令没有退出状态)。
- 第2.5.2节“特殊参数”表示
$?
“扩展到最新管道的小数退出状态(请参阅第2.9.2节)”。 - 第2.9.2节“管道”表示管道是一个或多个命令的序列,可选地(整体上)以
!
开头,并以|
连接命令。关于退出状态,它说如果流水线不是以
!
保留字开头,则退出状态应为流水线中指定的最后一条命令的退出状态。否则,退出状态应为最后一条命令的退出状态的逻辑非。 - 第2.9节“ Shell命令”定义了“命令”,并说“除非另有说明,否则命令的退出状态应为该命令执行的最后一个简单命令的退出状态”。 “命令”可以是以下任意一项:
- 一个简单命令(第2.9.1节),它只是一个外部程序,一个内置的Shell或一个变量赋值(没有命令名)。前者当然会返回所执行命令的退出状态。关于后者,规范说:
如果没有命令名称,但是命令包含命令替换,则命令应以最后执行的命令替换的退出状态完成。否则,该命令应以零退出状态完成。
- 如上文第2.9.2节所述的管道。
- 复合列表(第2.9.3节),它是一个或多个{一个或多个的序列{一个或多个管道的序列(参见上文)的序列,并由
&&
或{{ 1}}},每个以||
或;
终止(带有最后一个&
可选)},并由换行符组成。复合列表返回同步执行的最后一个管道的退出状态;异步执行的管道(以;
终止的管道)将退出状态设置为零。这些序列都不能为空,这确保将至少执行一个管道。 - 复合命令(第2.9.4节),该命令可以是:
- 子外壳或带括号的化合物列表(第2.9.4.1节),它返回基础化合物列表的退出状态(请参见上文)。
- 条件构造(
&
,§2.9.4.3或case
,§2.9.4.4)或循环(if
,§2.9.4.2;for
, §2.9.4.5;while
,§2.9.4.6)。如果未执行此构造的主体,则返回的退出状态为零。
- 函数定义(第2.9.5节),如果定义被接受,则返回退出状态为零,否则返回非零状态。
- 一个简单命令(第2.9.1节),它只是一个外部程序,一个内置的Shell或一个变量赋值(没有命令名)。前者当然会返回所执行命令的退出状态。关于后者,规范说:
- 最后,第2.9.4.4节“如果有条件的构造”将
until
构造的条件和主体定义为复合列表,如上文第2.9.3节所述。这意味着if
构造的主体将始终包含至少一个破坏if
的命令。该规范甚至没有为“实现定义的行为”留出任何摆动的空间。
以上所有内容都意味着编写保留$?
的无操作命令的唯一方法是读出$?
中的值并将其返回。 POSIX shell中有三种构造可以实现此目的,其中两个已经在问题正文中提到:来自子shell的$?
或函数调用。
第三个是exit
:@ Jens答案中提到的shell采购声明。通过提供仅包含.
命令的脚本,可以保留return "$?"
的值。但是,这需要您安排在某个已知的位置找到合适的脚本,我认为这与确保在文件中足够早地定义无操作功能(如果不是更多)一样不方便。
如果您稍微改变了严格的POSIX要求,即使可以克服,但是:
$?
. /dev/stdin <<EOF
return "$?"
EOF
不是POSIX功能,但可以广泛使用。它在IEEE Std 1003.1-2017 vol.1中明确列出。 1§2.1.1作为扩展。上面的代码段已经过测试,可以在Linux上的bash 5.0.8,破折号0.5.11和busybox sh 1.30.1中工作(具有适当的设置/dev/stdin
等)。
最简单的方法是利用简单的分配。不用:
,而要做_rc=$?
。
if condition; then
[ list-true ] # optional
_rc=$?
else
[ list-false ] # optional
_rc=$?
fi
( exit $_rc ) # optional
list-post-if
使用此变量_rc
,您已存储了最后执行的命令的退出状态,无论是condition
还是最后一个命令在list-true
或list-false
中。 / p>
- 支持此方法的参数是赋值的开销很低。
- 反对的观点是至少需要重写
list-post-if
来使用_rc
而不是$?
。 - 如果后者不可行或过于乏味,则可以考虑在条件语句之后添加一个
(exit $_rc)
。但是,这需要一个子外壳,但这只是一个。
不要让任何人告诉你只有功能和子shell。
您可以创建或分发另一个小文件吗?如果是这样,您只需使用
即可创建文件return $?
,然后将其作为保持退出状态的“空”命令获取:
$ echo 'return $?' > keepstatus
$ ls foobar
ls: fooobar: No such file or directory
$ . ./keepstatus
$ echo $?
2
$ sleep 100
^C
$ . ./keepstatus
$ echo $?
130
不进行分叉,不使用函数,没有额外的变量,保持状态,并且与POSIXly一样。
我什至还有第四种方法,当我牺牲分叉和假设的奖励积分时,由于您位于autoconf m4领域,查找和使用主机编译器很容易。
cat > keepstatus.c <<EOF
#include <stdlib.h>
int main(int argc,char **argv) {
return argv[1] ? atoi(argv[1]) : 0;
}
EOF
$CC -o keepstatus keepstatus.c
然后使用/path/to/keepstatus $?
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。