No-op shell命令保留$?

如何解决No-op shell命令保留$?

仅使用POSIX shell的功能,是否有一个不执行任何操作且不会更改$?的值的“简单命令” 人们通常将:描述为shell的无操作命令,但这总是将$?设置为零,所以这不是我想要的。

生成外壳程序脚本的程序需要此功能。在几个地方,它需要发出一个if-then-else块

if CONDITION
then
    TRUE-COMMANDS
else
    FALSE-COMMANDS
fi

但是,由于我不想立即解释的更多复杂性,因此无法可靠地判断TRUE-COMMANDSFALSE-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.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-truelist-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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-