7

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?

1 Answer 1

11

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 (e.g. ARRAY_VARIABLE[*]).

Sign up to request clarification or add additional context in comments.

6 Comments

Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0. So all that PEOPLE=$(...) did was assign the result of the command substitution to PEOPLE[0]. So there is no conversion, it's still an array that cannot be exported :)
@PesaThe - Not sure what you're referring to. Is there a specific line of code I wrote that doesn't work the way I said it would?
Ah, you're saying that the assignment of a string to an array variable just populates the 0-index of the array. Correct? So even if I do export PEOPLE="some string", it will only overwrite the 0-index of the PEOPLE array?
@NickChammas Right; once the array attribute on a name is set, assigning to the name alone will always assign to the 0th element of the array. You can't unset the array attribute (short of unsetting the name itself).
You can even see the minimal change necessary to turn a regular variable into an array. (This isn't strictly related to the question, but it's fun to watch happen, for some extremely minimal value of "fun".) After x=3, run declare -p x to see that it is a regular variable. Then run declare -a x followed by declare -p x. You'll see that the former value of x is now the value of the 0th element of the array x.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.