0

Okay, so I'm having trouble dealing with Bash and it's lack of global variables. The problem I'm having is that inside the while loop, I'm having to do a for loop, and inside that for loop I'm going to be calling a script and I need to dynamically change the variable $NAME. Unfortunately, I won't know what it is until I've tried a few different options. The variable never wants to change after it's been set in the original process. I've done a lot of research about this today and I've found that it's because reading a file in such a file opens a subshell and it can't edit values in it's parent process. I've tried making several files to source to export the variable dynamically, but that doesn't work either.

EDIT: THIS EXAMPLE IS TRIVIAL, BUT IT'S THE SAME LOGIC I'M USING AT WORK. CAN'T SHOW WORK CODE.

#!/usr/bin/env bash
FILE=$1
SCRIPT="FOO"
NAME="TEST"

#create script
cat > "${SCRIPT}" <<EOF
HELLO WORLD, $NAME
EOF

while read line; do
echo $line
echo $SCRIPT
  for i in `seq 1 10`; do
    (
    NAME="MEGAMAN"
    echo $NAME
    )
  done
done < "$FILE"
#echo $NAME
cat > "${SCRIPT}" <<EOF
HELLO WORLD, $NAME
EOF

My output:

jose@jose-desktop:~/workspace/Script $ ./Main FOO 
HELLO WORLD, TEST
FOO
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN
MEGAMAN **this output is fine

And the file it outputs:

HELLO WORLD, TEST **This file should say MEGAMAN.
user3505901
  • 408
  • 1
  • 6
  • 19
  • 1
    "Lack of global variables"? Huh? **All** variables are global in bash unless you declare them otherwise. – Charles Duffy Sep 20 '16 at 02:04
  • 1
    ...your problem is that you're running code in a subprocess, so the global variables you set there are cleared when that subprocess exits. See also [BashFAQ #24](http://mywiki.wooledge.org/BashFAQ/024). Same as with globals in any other language if you're forking off a subprocess. – Charles Duffy Sep 20 '16 at 02:05
  • ...the simplest solution is just not to do that -- which is to say, to ensure that the code that sets your variables is in-process. If you replaced your `( )`s with `{ }`s, you'd be set. – Charles Duffy Sep 20 '16 at 02:08
  • Aside: All-caps variable names are, [per POSIX specification](http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html), used for variables with meaning to the shell and operating system; the specification guarantees that applications can use any variable name with at least one lower-case letter without modifying the behavior of the shell or operating system. The spec section is on environment variables, but since setting a shell variable with a name already used by an environment variable overwrites the latter, the convention applies there as well. Consider `$name` vs `$NAME`. – Charles Duffy Sep 20 '16 at 02:13
  • Aside (2): `seq` is neither specified by POSIX nor shipped as part of bash. Consider using C-style for loops: `for ((i=0; i<10; i++))`, which avoids the fork/exec overhead to run an external tool. – Charles Duffy Sep 20 '16 at 02:15

1 Answers1

1

It is impossible to change a variable from a child process, be that a subshell or some other shell script. If you have to do something like that, you have to find a way for the subprocess to communicate with the parent. For example, if you are invoking an external script and you want to take a value computed by that script and store it in a variable in the parent script, you could have the script output that value in its stdout and then capture it in the parent process, e.g.

# child-script.sh:
myvar=.... # compute some value
echo "$myvar" # put that at the end of the script

and in the parent, replace the line

./child-script.sh

by

myvar="$(./child-script.sh)"
redneb
  • 21,794
  • 6
  • 42
  • 54
  • But what if I need to pass in the variable, the variable that I need to pass in is the variable from the for loop. I suppose in this case it'd be the numbers 1 through 10? I need that to be interpolated into the script as $NAME – user3505901 Sep 20 '16 at 02:09
  • @user3505901, `$()` runs a subshell. Subshells inherit all the state of their parent, including variables' current values. So yes, you can run `myvar=$(something involving "$i")` if you want to go that route (though I wouldn't do so unless you can't possibly avoid the subshell altogether). – Charles Duffy Sep 20 '16 at 02:10