简体   繁体   中英

Why is this Bash script not inheriting all environment variables?

I'm trying something very straightforward:

PEOPLE=(
  "nick"
  "bob"
)
export PEOPLE="$(IFS=, ; echo "${PEOPLE[*]}")"
echo "$PEOPLE"  # prints 'nick,bob'
./process-people.sh

For some reason, process-people.sh isn't seeing $PEOPLE . As in, if I echo "$PEOPLE" from inside process-people.sh , it prints an empty line.

From what I understand, the child process created by invoking ./process-people.sh should inherit all the parent process's environment variables, including $PEOPLE . Yet, I've tried this on both Bash 3.2.57(1)-release and 4.2.46(2)-release and it doesn't work.

What's going on here?

That's a neat solution you have there for joining the elements of a Bash array into a string . Did you know that in Bash you cannot export array variables to the environment ? And if a variable is not in the environment, then the child process will not see it.

Ah. But you aren't exporting an array, are you. You're converting the array into a string and then exporting that. So it should work.

But this is Bash! Where various accidents of history conspire to give you the finger.

As @PesaThe and @chepner pointed out in the comments below, you cannot actually convert a Bash array variable to a string variable . According to the Bash reference on arrays :

Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0.

So when you call export PEOPLE=... where PEOPLE was previously assigned an array value, what you're actually doing is PEOPLE[0]=... . Here's a fuller example:

PEOPLE=(
  "nick"
  "bob"
)
export PEOPLE="super"
echo "$PEOPLE"  # masks the fact that PEOPLE is still an array and just prints 'super'
echo "${PEOPLE[*]}"  # prints 'super bob'

It's unfortunate that the export silently fails to export the array to the environment (it returns 0 ), and it's confusing that Bash equates ARRAY_VARIABLE to ARRAY_VARIABLE[0] in certain situations. We'll just have to chalk that up to a combination of history and backwards compatibility.

Here's a working solution to your problem:

PEOPLE_ARRAY=(
  "nick"
  "bob"
)
export PEOPLE="$(IFS=, ; echo "${PEOPLE_ARRAY[*]}")"
echo "$PEOPLE"  # prints 'nick,bob'
./process-people.sh

The key here is to assign the array and derived string to different variables. Since PEOPLE is a proper string variable, it will export just fine and process-people.sh will work as expected.

It's not possible to directly change a Bash array variable into a string variable. Once a variable is assigned an array value, it becomes an array variable. The only way to change it back to a string variable is to destroy it with unset and recreate it.

Bash has a couple of handy commands for inspecting variables that are useful for investigating these kinds of issues:

printenv PEOPLE  # prints 'nick,bob'
declare -p PEOPLE_ARRAY  # prints: declare -ax PEOPLE_ARRAY='([0]="nick" [1]="bob")'

printenv will only return a value for environment variables, vs. echo , which will print a result whether the variable has been properly exported or not.

declare -p will show the full value of a variable, without the gotchas related to including or leaving out array index references (eg ARRAY_VARIABLE[*] ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM