Bash 中变量赋值到命令替换的退出代码

2024-10-22 08:28:00
admin
原创
83
摘要:问题描述:我对在执行变量赋值和使用命令替换时命令将返回什么错误代码感到困惑:a=$(false); echo $? 它输出1,这让我认为变量赋值不会清除或在最后一个变量赋值时产生新的错误代码。但是当我尝试这样做时:false; a=""; echo $? 它输出0,显然这就...

问题描述:

我对在执行变量赋值和使用命令替换时命令将返回什么错误代码感到困惑:

a=$(false); echo $?

它输出1,这让我认为变量赋值不会清除或在最后一个变量赋值时产生新的错误代码。但是当我尝试这样做时:

false; a=""; echo $?

它输出0,显然这就是a=""返回的内容,并且它会覆盖1返回的内容false

我想知道为什么会发生这种情况,变量赋值有什么特殊性吗?与其他普通命令不同吗?或者只是因为它a=$(false)被视为单个命令,只有命令替换部分有意义?

- 更新 -

谢谢大家,从答案和评论中我得到了这个观点:“当您使用命令替换分配变量时,退出状态就是命令的状态。”(来自@Barmar),这个解释非常清楚易懂,但是对于程序员来说说得不够精确,我想从TLDP或GNU手册页等权威机构那里看到对这一点的参考,请帮我找出来,再次感谢!


解决方案 1:

执行命令时,$(command)允许命令的输出替换其自身。

当你说:

a=$(false)             # false fails; the output of false is stored in the variable a

该命令产生的输出false存储在变量中a。此外,退出代码与命令产生的代码相同。 help false会告诉:

false: false
    Return an unsuccessful result.
    
    Exit Status:
    Always fails.

另一方面,说:

$ false                # Exit code: 1
$ a=""                 # Exit code: 0
$ echo $?              # Prints 0

导致返回分配的退出代码,a0


编辑:

引用手册中的话:

如果其中一个扩展包含命令替换,则该命令的退出状态是最后执行的命令替换的退出状态。

引用自BASHFAQ/002:

如何将命令的返回值和/或输出存储在变量中?

...

output=$(command)

status=$?

分配给 对的退出状态output没有影响command,该状态仍然在 中$?

这不是 bash 特有的。引用 The Open Group Base Specifications Issue 7, POSIX.1-2017 中“Shell & Utilities”卷 2.9.1 节“简单命令”的结尾:

如果没有命令名称,但命令包含命令替换,则命令将以最后执行的命令替换的退出状态完成

解决方案 2:

请注意,与 结合使用时情况并非如此local,如。即使失败,该表单也会成功退出。local variable="$(command)"`command`

以这个 Bash 脚本为例:

#!/bin/bash

function funWithLocalAndAssignmentTogether() {
    local output="$(echo "Doing some stuff.";exit 1)"
    local exitCode=$?
    echo "output: $output"
    echo "exitCode: $exitCode"
}

function funWithLocalAndAssignmentSeparate() {
    local output
    output="$(echo "Doing some stuff.";exit 1)"
    local exitCode=$?
    echo "output: $output"
    echo "exitCode: $exitCode"
}

funWithLocalAndAssignmentTogether
funWithLocalAndAssignmentSeparate

输出如下:

nick.parry@nparry-laptop1:~$ ./tmp.sh 
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1

这是因为local实际上是一个内置命令,而像 这样的命令替换 的输出后会调用。因此您可以从 获得退出状态。local variable="$(command)"`local command`local

解决方案 3:

我昨天(2018 年 8 月 29 日)遇到了同样的问题。

除了在Nick P. 的回答和 @sevko 在接受的答案local中的评论中提到的之外,在全局范围内也有同样的行为。declare

这是我的 Bash 代码:

#!/bin/bash

func1()
{
    ls file_not_existed
    local local_ret1=$?
    echo "local_ret1=$local_ret1"

    local local_var2=$(ls file_not_existed)
    local local_ret2=$?
    echo "local_ret2=$local_ret2"

    local local_var3
    local_var3=$(ls file_not_existed)
    local local_ret3=$?
    echo "local_ret3=$local_ret3"
}

func1

ls file_not_existed
global_ret1=$?
echo "global_ret1=$global_ret1"

declare global_var2=$(ls file_not_existed)
global_ret2=$?
echo "global_ret2=$global_ret2"

declare global_var3
global_var3=$(ls file_not_existed)
global_ret3=$?
echo "global_ret3=$global_ret3"

输出:

$ ./declare_local_command_substitution.sh 2>/dev/null 
local_ret1=2
local_ret2=0
local_ret3=2
global_ret1=2
global_ret2=0
global_ret3=2

local_ret2请注意上面输出中的和 的值global_ret2。退出代码被local和覆盖declare

我的 Bash 版本:

$ echo $BASH_VERSION 
4.4.19(1)-release

解决方案 4:

(这不是对原始问题的回答,但太长了,无法评论)

请注意输出 0!显然, devnull 的答案export A=$(false); echo $?中引用的规则不再适用。为该引文添加一些背景信息(重点是我的):

3.7.1 简单命令扩展

...

如果扩展后还有命令名,则执行按如下所述进行。否则,命令退出。如果其中一个扩展包含命令替换,则命令的退出状态是执行的最后一个命令替换的退出状态。如果没有命令替换,则命令以零状态退出。

3.7.2 命令搜索和执行[ —这是“以下”的情况]

IIUC 手册将其描述var=foo语法特例var=foo command...(相当令人困惑,从语义上讲它们非常不同!)。“最后一个命令替换的退出状态”规则仅适用于无命令的情况。

尽管很容易将其视为export var=foo“修改后的赋值语法”,但它并不是——它export是一个内置命令(只是恰好采用了类似赋值的参数)。

=> 如果您想要导出 var 并捕获命令替换状态,请分两个阶段进行:

A=$(false)
# ... check $?
export A

这种方式在set -e模式下也有效——如果命令替换返回非 0 则立即退出。

解决方案 5:

正如其他人所说,命令替换的退出代码是被替换命令的退出代码,因此

FOO=$(false)
echo $?
---
1

然而,出乎意料的是,export在开头添加却产生了不同的结果:

export FOO=$(false)
echo $?
---
0

这是因为,虽然替代的命令false失败,但export命令成功,并且这是语句返回的退出代码。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用