Set a parent shell's variable from a subshell

73,377

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

Solution 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

Solution 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"

Solution 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

Solution 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
Share:
73,377
Matt Joiner
Author by

Matt Joiner

About Me I like parsimonious code, with simple interfaces and excellent documentation. I'm not interested in enterprise, boiler-plate, or cookie-cutter nonsense. I oppose cruft and obfuscation. My favourite languages are Go, Python and C. I wish I was better at Haskell. Google+ GitHub Bitbucket Google code My favourite posts http://stackoverflow.com/questions/3609469/what-are-the-thread-limitations-when-working-on-linux-compared-to-processes-for/3705919#3705919 http://stackoverflow.com/questions/4352425/what-should-i-learn-first-before-heading-to-c/4352469#4352469 http://stackoverflow.com/questions/6167809/how-much-bad-can-be-done-using-register-variables-in-c/6168852#6168852 http://stackoverflow.com/questions/4141307/c-and-c-source-code-profiling-tools/4141345#4141345 http://stackoverflow.com/questions/3463207/how-big-can-a-malloc-be-in-c/3486163#3486163 http://stackoverflow.com/questions/4095637/memory-use-of-stl-data-structures-windows-vs-linux/4183178#4183178

Updated on July 05, 2022

Comments

  • Matt Joiner
    Matt Joiner almost 2 years

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

    a=3
    (a=4)
    echo $a
    
  • chepner
    chepner about 11 years
    Although less dangerous, I'd say this should be used about as frequently as a fork bomb in an actual script.
  • Christian
    Christian over 10 years
    Thank you for pointing out the curly braces. I had seen them, but never knew what they did nor use them. Good advice!
  • Benoit Duffez
    Benoit Duffez over 9 years
    Not pretty, but pretty much the better option in some cases. Thanks.
  • aroth
    aroth over 8 years
    The file approach is quite useful if you're using a subshell to collect the values for multiple variables that the parent shell will need later, particularly in cases where not using a subshell is nontrivial.
  • dingalapadum
    dingalapadum over 8 years
    I had to ..."attach $PPID"... in order to make this work - $$ contains the PID of the current shell.
  • BeniBela
    BeniBela over 7 years
    @dingalapadum not within OP's parenthesis. Probably the subshell runs in the same process and just resets the variables afterwards
  • dan_linder
    dan_linder about 6 years
    I had to use this because I use "set -u" and "set -e" at the start of my main script, but I needed to run some commands to set a variable that exit with a non-zero exit code (and thus triggering the "-e" to kill the script).
  • ArchimedesMP
    ArchimedesMP almost 6 years
    Is there any way to use this when piping? E.g. { false; a=$?;} | true will not set the global(?) a to the desired value :(
  • cdarke
    cdarke almost 6 years
    @ArchimedesMP: A pipe is used to pass data between processes using standard streams, there are no processes involved here (except the shell). What is it you are trying to achieve?
  • ArchimedesMP
    ArchimedesMP almost 6 years
    I wanted to filter a meaningless warning from a third party tools stderr inside a script, without messing up stderr/stdout and while returning the original exit code. E.g. { some-tool 2>&1 1>&3; ec=$?; } | grep -v "Ignore some warning" 3>&1 1>&2; exit $ec (the X>&Y might be a bit off since I can not access the script right now). [& thanks for the reply on a 4y old comment!]
  • cdarke
    cdarke almost 6 years
    @ArchimedesMP: sounds a bit complex to do in one line, I would go for a wrapper script or a function to filter it out (you can redirect a function, e.g. myfunc() { echo stuff and nonsense; } > gash.txt)
  • Ajeetkumar
    Ajeetkumar over 5 years
    Hi @Carl. this works but can explanation to the answer?
  • Carl Bosch
    Carl Bosch over 5 years
    @Ajeetkumar Please find a link to the unix.stackexchange.com/a/114301 . is a shortcut to source cammand ss64.com/bash/source.html
  • ivan_pozdeev
    ivan_pozdeev about 5 years
    Prone to race conditions since access to files is not synchronized.
  • ivan_pozdeev
    ivan_pozdeev about 5 years
    calledScript.sh is run in the current shell, so it doesn't qualify as "subshell".
  • ivan_pozdeev
    ivan_pozdeev about 5 years
    @BeniBela in a () subshell, $$ remains the same. The actual subshell's PID can be seen with $BASHPID.
  • Carl Bosch
    Carl Bosch about 5 years
    Point taken ivan_pozdeev, technically I did not answer to the question as it is was asked
  • opinion_no9
    opinion_no9 about 3 years
    can this be done more elegant with file descriptors? Are file descriptors accessible from sub shell and parent shell the like?
  • Ale
    Ale almost 3 years
    Using read allows to pass several variables. The subshell inherits variables from the parent, modifies them as needed, before exiting prints them out. That way it's easy to insolate a large span of code, for example the body of a loop.
  • erikbstack
    erikbstack over 2 years
    if you source the shell script there is no subshell anymore
  • andreagalle
    andreagalle about 2 years
    what about this workaround? stackoverflow.com/a/15383353/6466510
  • midnite
    midnite about 2 years
    I am looking for a more elegant way similar to this approach too. How to use file descriptors for this? Is it the same way as khachik's answer below writing to output streams? I suppose that limits to one variable only.