从子shell返回值并输出到局部变量

Posted

技术标签:

【中文标题】从子shell返回值并输出到局部变量【英文标题】:return value from subshell and output to local variables 【发布时间】:2012-10-11 13:32:01 【问题描述】:

我发现了我无法解释的奇怪行为。 以下代码可以正常工作:

function prepare-archive 
blah-blah-blah...
_SPEC_FILE=$(check-spec-file "$_GIT_DIR/packaging/")
exit $?
blah-blah-blah...

表示我得到了我期望的价值:

bash -x ./this-script.sh:
++ exit 1
+ _SPEC_FILE='/home/likern/Print/Oleg/print-service/packaging/print-service.spec
/home/likern/Print/Oleg/print-service/packaging/print-service2.spec'
+ exit 1

只要我将local 定义添加到变量:

local _SPEC_FILE=$(check-spec-file "$_GIT_DIR/packaging/")

我得到以下信息:

bash -x ./this-script.sh:
++ exit 1
+ local '_SPEC_FILE=/home/likern/Print/Oleg/print-service/packaging/print-service.spec
/home/likern/Print/Oleg/print-service/packaging/print-service2.spec'
+ exit 0
$:~/MyScripts$ echo $?
0

问题:为什么?发生了什么?我可以将 subshel​​l 的输出捕获到 local 变量并可靠地检查 subshel​​l 的返回值吗?

P.S.prepare-archive 在主 shell 脚本中被调用。第一个exit 是来自check-spec-file 函数的exit,第二个来自prepare-archive 函数——这个函数本身是从主shell 脚本执行的。我通过exit 1check-spec-file 返回值,然后将此值传递给exit $?。因此,我希望它们应该是相同的。

【问题讨论】:

在什么情况下调用prepare-archive++ exit 1 不适合您显示的任何代码。 【参考方案1】:

要捕获子shell的退出状态,在赋值前将变量声明为local,例如下面的脚本

#!/bin/sh

local_test()

    local local_var
    local_var=$(echo "hello from subshell"; exit 1)
    echo "subshell exited with $?"
    echo "local_var=$local_var"


echo "before invocation local_var=$local_var in global scope"
local_test
echo "after invocation local_var=$local_var in global scope"

产生以下输出

before invocation local_var= in global scope
subshell exited with 1
local_var=hello from subshell
after invocation local_var= in global scope

【讨论】:

您介意解释一下这里发生了什么吗?您的回答解决了我的问题,但我仍然不明白它是如何以及为什么这样做的。 @IhorKaharlichenko 如果在声明中使用local 分配变量,则子shell 退出状态将被local 内置命令的退出状态“屏蔽”/覆盖。在赋值之前声明一个局部变量也更便于移植(一些 shell 不支持使用local 进行初始化)。【参考方案2】:

来自 bash 手册,Shell Builtin Commands 部分:

local:
    [...]The return status is zero unless local is used outside a function, an invalid name is supplied, or name is a readonly variable. 

希望这会有所帮助 =)

【讨论】:

这是否意味着变量定义在没有local的情况下使用时没有返回状态?为什么?你知道一些解决方法吗 - 因为我不想产生多余的全局变量。 我不知道任何不涉及创建全局变量或使用文件的解决方法,抱歉 =(。我会这样做 local TMPFILE=$(mktemp); check-spec-file > $TMPFILE; local error=$?; local _SPEC_FILE=$(cat $TMPFILE); rm $TMPFILE @Mephi_stofel - 要解决这个问题,您可以拆分声明和初始化,例如首先是:local my_var,然后是:my_var=$(my_function)。初始化将安全地引用局部变量,而不会污染全局范围。 谢谢你,@EliranMalka!我今天遇到了同样的问题,这正是我解决的方法。 调试一小时后发现这个问题真是太棒了,然后你才知道如何在 Google/SO 上搜索。 ;)【参考方案3】:

当我使用 bash 子shell 括号对许多 echo 命令进行分组时,我遇到了这个奇怪的问题。 在我的情况下,我只需要将一个值传回调用 shell,所以我只使用了 exit 命令

RET=0
echo RET: $RET
(echo hello
echo there
RET=123
echo RET: $RET
exit $RET)
RET=$?
echo RET: $RET

给出以下输出

RET: 0
hello
there
RET: 123
RET: 123

如果没有退出命令,你会得到这个令人困惑的:

RET: 0
hello
there
RET: 123
RET: 0

【讨论】:

以上是关于从子shell返回值并输出到局部变量的主要内容,如果未能解决你的问题,请参考以下文章

shell中的局部变量与全局变量

shell入门练习

如何从 TempTable 中检索值并在局部变量中设置该值

shell 全局和局部变量

Shell之环境变量、局部变量

将插入的输出值存储._ID 到局部变量以在另一个查询中重用它