Exporting an array in bash script

2020-01-24 12:24发布

I can not export an array from a bash script to another bash script like this:

export myArray[0]="Hello"
export myArray[1]="World"

When I write like this there are no problem:

export myArray=("Hello" "World")

For several reasons I need to initialize my array into multiple lines. Do you have any solution?

10条回答
迷人小祖宗
2楼-- · 2020-01-24 12:26

For arrays with values without spaces, I've been using a simple set of functions to iterate through each array element and concatenate the array:

_arrayToStr(){
    array=($@)

    arrayString=""
    for (( i=0; i<${#array[@]}; i++ )); do
        if [[ $i == 0 ]]; then
            arrayString="\"${array[i]}\""
        else
            arrayString="${arrayString} \"${array[i]}\""
        fi
    done

    export arrayString="(${arrayString})"
}

_strToArray(){
    str=$1

    array=${str//\"/}
    array=(${array//[()]/""})

    export array=${array[@]}
}

The first function with turn the array into a string by adding the opening and closing parentheses and escaping all of the double quotation marks. The second function will strip the quotation marks and the parentheses and place them into a dummy array.

In order export the array, you would pass in all the elements of the original array:

array=(foo bar)
_arrayToStr ${array[@]}

At this point, the array has been exported into the value $arrayString. To import the array in the destination file, rename the array and do the opposite conversion:

_strToArray "$arrayName"
newArray=(${array[@]})
查看更多
放荡不羁爱自由
3楼-- · 2020-01-24 12:30

The environment is just a collection of key-value pairs, both of which are character strings. A proper solution that works for any kind of array could either

  • Save each element in a different variable (e.g. MY_ARRAY_0=myArray[0]). Gets complicated because of the dynamic variable names.
  • Save the array in the file system (declare -p myArray >file).
  • Serialize all array elements into a single string.

These are covered in the other posts. If you know that your values never contain a certain character and your keys are consecutive integers, you can simply save the array as a delimited list:

export MY_ARRAY=$(IFS='|'; echo "${myArray[*]}")

And restore it in the child process:

IFS='|' myArray=($MY_ARRAY)
查看更多
家丑人穷心不美
4楼-- · 2020-01-24 12:36

I was editing a different post and made a mistake. Augh. Anyway, perhaps this might help? https://stackoverflow.com/a/11944320/1594168

Note that because the shell's array format is undocumented on bash or any other shell's side, it is very difficult to return a shell array in platform independent way. You would have to check the version, and also craft a simple script that concatinates all shell arrays into a file that other processes can resolve into.

However, if you know the name of the array you want to take back home then there is a way, while a bit dirty.

Lets say I have

MyAry[42]="whatever-stuff";
MyAry[55]="foo";
MyAry[99]="bar";

So I want to take it home

name_of_child=MyAry
take_me_home="`declare -p ${name_of_child}`";
export take_me_home="${take_me_home/#declare -a ${name_of_child}=/}"

We can see it being exported, by checking from a sub-process

echo ""|awk '{print "from awk =["ENVIRON["take_me_home"]"]";  }'

Result :

from awk =['([42]="whatever-stuff" [55]="foo" [99]="bar")']

If we absolutely must, use the env var to dump it.

env > some_tmp_file

Then

Before running the another script,

# This is the magic that does it all
source some_tmp_file
查看更多
Summer. ? 凉城
5楼-- · 2020-01-24 12:37

As lesmana reported, you cannot export arrays. So you have to serialize them before passing through the environment. This serialization useful other places too where only a string fits (su -c 'string', ssh host 'string'). The shortest code way to do this is to abuse 'getopt'

# preserve_array(arguments). return in _RET a string that can be expanded
# later to recreate positional arguments. They can be restored with:
#   eval set -- "$_RET"
preserve_array() {
    _RET=$(getopt --shell sh --options "" -- -- "$@") && _RET=${_RET# --}
}

# restore_array(name, payload)
restore_array() {
   local name="$1" payload="$2"
   eval set -- "$payload"
   eval "unset $name && $name=("\$@")"
}

Use it like this:

foo=("1: &&& - *" "2: two" "3: %# abc" )
preserve_array "${foo[@]}"
foo_stuffed=${_RET}
restore_array newfoo "$foo_stuffed"
for elem in "${newfoo[@]}"; do echo "$elem"; done

## output:
# 1: &&& - *
# 2: two
# 3: %# abc

This does not address unset/sparse arrays. You might be able to reduce the 2 'eval' calls in restore_array.

查看更多
倾城 Initia
6楼-- · 2020-01-24 12:38

Jeez. I don't know why the other answers made this so complicated. Bash has nearly built-in support for this.

In the exporting script:

myArray=( '  foo"bar  ' $'\n''\nbaz)' )  # an array with two nasty elements

myArray="${myArray[@]@Q}" ./importing_script.sh

(Note, the double quotes are necessary for correct handling of whitespace within array elements.)

Upon entry to importing_script.sh, the value of the myArray environment variable comprises these exact 26 bytes:

'  foo"bar  ' $'\n\\nbaz)'

Then the following will reconstitute the array:

eval "myArray=( ${myArray} )"

CAUTION! Do not eval like this if you cannot trust the source of the myArray environment variable. This trick exhibits the "Little Bobby Tables" vulnerability. Imagine if someone were to set the value of myArray to ) ; rm -rf / #.

查看更多
虎瘦雄心在
7楼-- · 2020-01-24 12:41

Much thanks to @stéphane-chazelas who pointed out all the problems with my previous attempts, this now seems to work to serialise an array to stdout or into a variable.

This technique does not shell-parse the input (unlike declare -a/declare -p) and so is safe against malicious insertion of metacharacters in the serialised text.

Note: newlines are not escaped, because read deletes the \<newlines> character pair, so -d ... must instead be passed to read, and then unescaped newlines are preserved.

All this is managed in the unserialise function.

Two magic characters are used, the field separator and the record separator (so that multiple arrays can be serialized to the same stream).

These characters can be defined as FS and RS but neither can be defined as newline character because an escaped newline is deleted by read.

The escape character must be \ the backslash, as that is what is used by read to avoid the character being recognized as an IFS character.

serialise will serialise "$@" to stdout, serialise_to will serialise to the varable named in $1

serialise() {
  set -- "${@//\\/\\\\}" # \
  set -- "${@//${FS:-;}/\\${FS:-;}}" # ; - our field separator
  set -- "${@//${RS:-:}/\\${RS:-:}}" # ; - our record separator
  local IFS="${FS:-;}"
  printf ${SERIALIZE_TARGET:+-v"$SERIALIZE_TARGET"} "%s" "$*${RS:-:}"
}
serialise_to() {
  SERIALIZE_TARGET="$1" serialise "${@:2}"
}
unserialise() {
  local IFS="${FS:-;}"
  if test -n "$2"
  then read -d "${RS:-:}" -a "$1" <<<"${*:2}"
  else read -d "${RS:-:}" -a "$1"
  fi
}

and unserialise with:

unserialise data # read from stdin

or

unserialise data "$serialised_data" # from args

e.g.

$ serialise "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party   Party   Party:

(without a trailing newline)

read it back:

$ serialise_to s "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
$ unserialise array "$s"
$ echo "${array[@]/#/$'\n'}"

Now is the time 
For all good men 
To drink $drink 
At the `party` 
Party   Party   Party

or

unserialise array # read from stdin

Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:

serialise_array "${my_array[@]}"

You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:

while unserialise array
do ...
done
查看更多
登录 后发表回答