我该如何在子shell中设置一个变量,使得它能够影响到父shell?
a=3
(a=4)
echo $a
我该如何在子shell中设置一个变量,使得它能够影响到父shell?
a=3
(a=4)
echo $a
a=3
{ a=4;}
echo $a
4
(注意其中的空格)。或者,将变量值写入stdout并在调用者中捕获它。a=3
a=$(a=4;echo $a)
echo $a
{ false; a=$?;} | true
将无法将全局变量 a
设置为所需的值 :( - ArchimedesMP有一个GDB-Bash变量的技巧:
gdb --batch-silent -ex "attach $$" -ex 'set bind_variable("a", "4", 0)';
虽然这总是在全局范围内设置一个变量,而不仅仅是父级作用域
..."attach $PPID"...
来使它工作 - $$ 包含当前 shell 的 PID。 - dingalapadum()
子 shell 中,$$
保持不变。实际子 shell 的 PID 可以通过 $BASHPID
查看。 - ivan_pozdeev不行。子Shell无法访问其父进程的环境变量。(至少在Bash提供的抽象层级内是如此。你可以尝试使用 gdb
或者踩JMP指令等方法来偷偷访问,但我并不建议这样做。)
一个可行的替代方案是,子Shell将赋值语句写入一个临时文件供其父进程读取:
a=3
(echo 'a=4' > tmp)
. tmp
rm tmp
echo "$a"
如果问题与 while 循环有关,修复的方法之一是使用进程替换:
var=0
while read i;
do
# perform computations on $i
((var++))
done < <(find . -type f -name "*.bin" -maxdepth 1)
bash
,而不是sh
(POSIX)。 - virtualdj通过阅读 @ruakh(谢谢)的答案,并考虑到评论中提出的文件描述符解决方案,我得到了以下想法:
a=3
. <(echo a=4; echo b=5)
echo $a
echo $b
结果:
4
5
+ a=3
+ . /dev/fd/63 # <-- the file descriptor ;)
++ echo a=4
++ echo b=5
++ a=4
++ b=5
+ echo 4
4
+ echo 5
5
a=3
echo $a
. ./calledScript.sh
echo $a
a=4
3
4
calledScript.sh
在当前 shell 中运行,因此它不符合“子 shell”的条件。 - ivan_pozdeev不要从父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
read
可以传递多个变量。子 shell 继承父 shell 的变量,根据需要进行修改,并在退出之前将它们打印出来。这样就可以轻松地隔离大量的代码,例如循环体。 - Ale# subshell.sh
echo Value
# caller
myvar=$(subshell.sh)
# 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
虽然从子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
这是我最终使用的技术,而不是设置多个变量来获取子shell FOO=$(myfunc)
的工作。
除非您可以将所有io应用到管道并使用文件句柄,否则在$(command)和任何其他子进程中无法完成基本的变量更新。
然而,常规文件是bash正常顺序处理的全局变量。注意:由于竞态条件,这种简单方法不适用于并行处理。
创建一个像这样的设置/获取/默认函数:
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会出错。它还可以应用其他的get/set逻辑。否则,它根本不需要。
一个丑陋的read方法避免了cat的不存在文件错误:
v="$(cat "$aRam/globalVariable 2>/dev/null")"