Reputation: 6706
This is my (not working) setup:
one.sh:
#!/bin/bash -x
MYFLAGS="FLAG=foo"
echo ${MYFLAGS} | ${MYFLAGS} ./two.sh
two.sh:
#!/bin/bash -x
echo $FLAG
output:
>> one.sh
+ MYFLAGS=FLAG=foo
+ echo FLAG=foo
+ FLAG=foo ./two.sh
./one.sh: line 5: FLAG=foo: command not found
How do I pass the list (in this casse only containing one element) of variables to the second script two.sh
? I know, I could use export FLAG
to make it available but I would like to avoid that.
Upvotes: 0
Views: 897
Reputation: 295403
Shell variables can contain arbitrary C strings, and consequently can contain any character except NUL. Thus, any character you might put inside a variable that contained multiple other variables... could also be a valid value, and is thus unsafe to use as a generic delimiter.
There are some fairly straightforward approaches available to circumvent this:
Some of the more obvious means of encoding require using eval
at decode time. This incurs security risks, and is thus frowned on. Workarounds to read eval
-style data formats exist, but some have significant bugs/restrictions (xargs
parses a subset of bash's string-literal formats, but not the full set).
The below approaches are selected to allow all possible values to be represented, and to avoid any security concerns.
To emit a NUL-delimited stream of key=value
pairs (note that you can't save this in a single variable -- as the values are terminated by NUL and a literal NUL can't be stored in a string -- but you can pass it over a file descriptor):
emit_variables() {
local var
for var; do
printf '%q=%s\0' "${var}" "${!var}"
done
}
To consume it:
read_variables() {
local varspec
while IFS= read -r -d '' varspec; do
name=${varspec%%=*}
value=${varspec#*=}
printf -v "$name" '%s' "$value"
done
}
Thus:
# make both these functions available through the environment
export -f emit_variables read_variables
foo=$'bar\nbaz' bar='baz=qux'
emit_variables foo bar | bash -c 'read_variables; declare -p foo bar'
...will properly emit:
declare -- foo="bar
baz"
declare -- bar="baz=qux"
Storing that content in a variable requires escaping the data in a way that doesn't disturb the NULs. For example:
export variable_state=$(emit_variables foo bar | openssl enc -base64)
...and, it your consumer:
read_variables < <(openssl enc -base64 -d <<<"$variable_state")
Upvotes: 3