繁体   English   中英

从子 shell 设置父 shell 的变量

[英]Set a parent shell's variable from a subshell

如何从子 shell 中在父 shell 中设置变量?

a=3
(a=4)
echo $a

子shell 的全部意义在于它不会影响调用会话。 在 bash 中,子 shell 是一个子进程,其他 shell 有所不同,但即便如此,子 shell 中的变量设置也不会影响调用者。 根据定义。

你需要一个子shell吗? 如果您只需要一个组,请使用大括号:

a=3
{ a=4;}
echo $a

给出4 (注意其中的空格)。 或者,将变量值写入标准输出并在调用者中捕获它:

a=3
a=$(a=4;echo $a)
echo $a

避免使用反引号``,它们已被弃用并且可能难以阅读。

有 gdb-bash-variable hack:

gdb --batch-silent -ex "attach $$" -ex 'set bind_variable("a", "4", 0)'; 

虽然这总是在全局范围内设置一个变量,而不仅仅是父范围

你没有。 子shell 无权访问其父环境。 (至少在 Bash 提供的抽象范围内。您可能会尝试使用gdb ,或粉碎堆栈等,以秘密获得此类访问权限。不过,我不建议这样做。)

一种替代方法是子外壳将赋值语句写入临时文件以供其父级读取:

a=3
(echo 'a=4' > tmp)
. tmp
rm tmp
echo "$a"

如果问题与 while 循环有关,解决此问题的一种方法是使用 Process Substitution:

    var=0
    while read i;
    do
      # perform computations on $i
      ((var++))
    done < <(find . -type f -name "*.bin" -maxdepth 1)

如此处所示: https ://stackoverflow.com/a/13727116/2547445

要更改从父脚本调用的脚本中的变量,您可以调用以“.”开头的脚本。

a=3
echo $a    
. ./calledScript.sh
echo $a

在称为Script.sh

a=4

预期产出

3
4

通过使用临时文件方法阅读@ruakh (谢谢)的答案以及要求文件描述符解决方案的评论,我得到了以下想法:

a=3    
. <(echo a=4; echo b=5)
echo $a
echo $b
  • 它允许一次返回不同的变量(这可能是已接受答案的子shell变体中的一个问题)。
  • 不需要迭代,
  • 没有临时文件需要处理。
  • 接近 OP 提出的语法。

结果:

4
5

启用 xtrace 后,可以看到我们从为子 shell 的输出创建的文件描述符中获取资源:

+ a=3
+ . /dev/fd/63 # <-- the file descriptor ;)
++ echo a=4
++ echo b=5
++ a=4
++ b=5
+ echo 4
4
+ echo 5
5

您可以在子shell中输出值并将子shell输出分配给调用者脚本中的变量:

# subshell.sh
echo Value

# caller
myvar=$(subshell.sh)

如果 subshel​​l 有更多输出,您可以通过将变量值和其他消息重定向到不同的输出流来分离它们:

# subshell.sh
echo "Writing value" 1>&2
echo Value

# caller
myvar=$(subshell.sh 2>/dev/null) # or to somewhere else
echo $myvar

或者,您可以在子 shell 中输出变量分配,在调用者脚本中评估它们并避免使用文件来交换信息:

# subshell.sh
echo "a=4"

# caller
# export $(subshell.sh) would be more secure, since export accepts name=value only.
eval $(subshell.sh)
echo $a

我能想到的最后一种方法是使用退出代码,但这仅涵盖整数值交换(并且在有限的范围内)并打破了解释退出代码的约定(0 表示成功,其他所有内容均为 0)。

除非您可以将所有 io 应用到管道并使用文件句柄,否则在 $(command) 和任何其他子进程中基本的变量更新是不可能的。

然而,常规文件是 bash 用于常规顺序处理的全局变量。 注意:由于竞争条件,这种简单的方法不适合并行处理。

像这样创建一个 set/get/default 函数:

globalVariable() { # NEW-VALUE
    # set/get/default globalVariable
    if [ 0 = "$#" ]; then
        # new value not given -- echo the value
        [ -e "$aRam/globalVariable" ] \
            && cat "$aRam/globalVariable" \
            || printf "default-value-here"
    else
        # new value given -- set the value
        printf "%s" "$1" > "$aRam/globalVariable"
    fi
}

"$aRam" 是存储值的目录。 我喜欢它是速度和波动性的 ram 磁盘:

aRam="$(mktemp -td $(basename "$0").XXX)" # temporary directory
mount -t tmpfs ramdisk "$aRam" # mount the ram disk there
trap "umount "$aRam" && rm -rf "$aRam"" EXIT # auto-eject

要读取值:

v="$(globalVariable)" # or part of any command

要设置值:

globalVariable newValue # newValue will be written to file

要取消设置值:

rm -f "$aRam/globalVariable"

访问函数的唯一真正原因是应用默认值,因为 cat 会在文件不存在的情况下出错。 应用其他获取/设置逻辑也很有用。 否则,根本不需要它。

避免 cat 不存在的文件错误的丑陋读取方法:

v="$(cat "$aRam/globalVariable 2>/dev/null")"

这种混乱的一个很酷的功能是您可以在程序运行时打开另一个终端并检查文件的内容。

不要从父 shell 访问变量,而是更改命令的顺序并使用进程替换

a=3
echo 5 | (read a)
echo $a

打印 3

a=3
read a < <(echo 5)
echo $a

打印 5

另一个例子:

let i=0 
seq $RANDOM | while read r
              do 
                let i=r 
              done
echo $i

对比

let i=0
while read r 
do 
  let i=r 
done < <(seq $RANDOM)
echo $i

或者,当作业控制处于非活动状态时(例如在脚本中),您可以使用lastpipe shell 选项来实现相同的结果,而无需更改命令的顺序:

#!/bin/bash
shopt -s lastpipe
let i=0
seq $RANDOM | while read r
              do 
                let i=r
              done
echo $i

虽然从子 shell 中获取多个变量比较困难,但您可以在函数内设置多个变量,而无需使用全局变量。

您可以将变量的名称传递给使用local -n将其转换为称为nameref的特殊变量的函数:

myfunc() {
    local -n OUT=$1
    local -n SIDEEFFECT=$2
    OUT='foo'
    SIDEEFFECT='bar'
}

myfunc A B
echo $A
> foo
echo $B
> bar

这是我最终使用的技术,而不是让 subshel​​l FOO=$(myfunc)工作设置多个变量。

一个非常简单实用的允许多个变量的方法如下,最终可能会在调用中添加参数:

function ComplexReturn(){
  # do your processing... 
  a=123
  b=456
  echo -n "AAA=${a}; BBB=${b};"
}
# ... this can be internal function or any subshell command
eval $(ComplexReturn)
echo $AAA $BBB

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM