Set a parent shell's variable from a subshell

北城以北 提交于 2019-11-26 09:08:44

问题


How do I set a variable in the parent shell, from a subshell?

a=3
(a=4)
echo $a

回答1:


The whole point of a subshell is that it doesn't affect the calling session. In bash a subshell is a child process, other shells differ but even then a variable setting in a subshell does not affect the caller. By definition.

Do you need a subshell? If you just need a group then use braces:

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

gives 4 (be careful of the spaces in that one). Alternatively, write the variable value to stdout and capture it in the caller:

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

avoid using back-ticks ``, they are deprecated and can be difficult to read.




回答2:


There is the gdb-bash-variable hack:

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

although that always sets a variable in the global scope, not just the parent scope




回答3:


You don't. The subshell doesn't have access to its parent's environment. (At least within the abstraction that Bash provides. You could potentially try to use gdb, or smash the stack, or whatnot, to gain such access clandestinely. I wouldn't recommend that, though.)

One alternative is for the subshell to write assignment statements to a temporary file for its parent to read:

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



回答4:


If the problem is related to a while loop, one way to fix this is by using Process Substitution:

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

as shown here: https://stackoverflow.com/a/13727116/2547445




回答5:


To change variables in a script called from a parent script, you can call the script preceded with a "."

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

in calledScript.sh

a=4

Expected output

3
4



回答6:


You can output the value in the subshell and assign the subshell output to a variable in the caller script:

# subshell.sh
echo Value

# caller
myvar=$(subshell.sh)

If the subshell has more to output you can separate the variable value and other messages by redirecting them into different output streams:

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

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

Alternatively, you can output variable assignments in the subshell, evaluate them in the caller script and avoid using files to exchange information:

# subshell.sh
echo "a=4"

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

The last way I can think of is to use exit codes but this covers the integer values exchange only (and in a limited range) and breaks the convention for interpreting exit codes (0 for success non-0 for everything else).




回答7:


Unless you can apply all io to pipes and use file handles, basic variable updating is impossible within $(command) and any other sub-process.

Regular files, however, are bash's global variables for normal sequential processing. Note: Due to race conditions, this simple approach is not good for parallel processing.

Create an set/get/default function like this:

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" is the directory where values are stored. I like it to be a ram disk for speed and volatility:

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

To read the value:

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

To set the value:

globalVariable newValue # newValue will be written to file

To unset the value:

rm -f "$aRam/globalVariable"

The only real reason for the access function is to apply a default value because cat will error given a non-existent file. It is also useful to apply other get/set logic. Otherwise, it would not be needed at all.

An ugly read method avoiding cat's non-existent file error:

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

A cool feature of this mess is that you can open another terminal and examine the contents of the files while the program is running.



来源:https://stackoverflow.com/questions/15541321/set-a-parent-shells-variable-from-a-subshell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!