Set a parent shell's variable from a subshell

后端 未结 7 1431
野性不改
野性不改 2020-11-27 03:58

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

a=3
(a=4)
echo $a
相关标签:
7条回答
  • 2020-11-27 04:12

    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
    
    0 讨论(0)
  • 2020-11-27 04:14

    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.

    0 讨论(0)
  • 2020-11-27 04:16

    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

    0 讨论(0)
  • 2020-11-27 04:16

    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).

    0 讨论(0)
  • 2020-11-27 04:17

    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"
    
    0 讨论(0)
  • 2020-11-27 04:33

    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

    0 讨论(0)
提交回复
热议问题