Reputation: 453
suppose I have a script called 'Hello'. something like:
array[0]="hello world"
array[1]="goodbye world"
echo ${array[*]}
and I want to do something like this in another script:
tmp=(`Hello`)
the result I need is:
echo ${tmp[0]} #prints "hello world"
echo ${tmp[1]} #prints "goodbye world"
instead I get
echo ${tmp[0]} #prints "hello"
echo ${tmp[1]} #prints "world"
or in other words, every word is put in a different spot in the tmp array. how do I get the result I need?
Upvotes: 2
Views: 3599
Reputation: 241931
Although there are workarounds, you can't really "return" an array from a bash function or script, since the normal way of "returning" a value is to send it as a string to stdout
and let the caller capture it with command substitution. [Note 1] That's fine for simple strings or very simple arrays (such as arrays of numbers, where the elements cannot contain whitespace), but it's really not a good way to send structured data.
There are workarounds, such as printing a string with specific delimiters (in particular, with NUL bytes) which can be parsed by the caller, or in the form of an executable bash statement which can be evaluated by the caller with eval
, but on the whole the simplest mechanism is to require that the caller provide the name of an array variable into which the value can be placed. This only works with bash functions, since scripts can't modify the environment of the caller, and it only works with functions called directly in the parent process, so it won't work with pipelines. Effectively, this is a mechanism similar to that used by the read
built-in, and a few other bash
built-ins.
Here's a simple example. The function split
takes three arguments: an array name, a delimiter, and a string:
split () {
IFS=$2 read -a "$1" -r -d '' < <(printf %s "$3")
}
eg:
$ # Some text
$ lorem="Lorem ipsum dolor
sit amet, consectetur
adipisicing elit, sed do
eiusmod tempor incididunt"
# Split at the commas, putting the pieces in the array phrase
$ split phrase "," "$lorem"
# Print the pieces in a way that you can see the elements.
$ printf -- "--%s\n" "${phrase[@]}"
--Lorem ipsum dolor
sit amet
-- consectetur
adipisicing elit
-- sed do
eiusmod tempor incididunt
Notes:
return
and exit
special forms. However, the status return mostly works as a boolean value, and certainly cannot carry a structured value.Upvotes: 3
Reputation: 247210
declare -a array # declares a global array variable
array=(
"hello world"
"goodbye world"
)
. hello.sh
tmp=( "${array[@]}" ) # if you need to make a copy of the array
echo "${tmp[0]}"
echo "${tmp[1]}"
If you truly want a function to spit out values that your script will capture, do this:
#!/bin/bash
array=(
"hello world"
"goodbye world"
)
printf "%s\n" "${array[@]}"
#!/bin/bash
./hello.sh | {
readarray -t tmp
echo "${tmp[0]}"
echo "${tmp[1]}"
}
# or
readarray -t tmp < <(./hello.sh)
echo "${tmp[0]}"
echo "${tmp[1]}"
Upvotes: 1
Reputation: 295954
Emit it as a NUL-delimited stream:
printf '%s\0' "${array[@]}"
...and, in the other side, read from that stream:
array=()
while IFS= read -r -d '' entry; do
array+=( "$entry" )
done
This often comes in handy in conjunction with process substitution; in the below example, the initial code is in a command (be it a function or an external process) invoked as generate_an_array
:
array=()
while IFS= read -r -d '' entry; do
array+=( "$entry" )
done < <(generate_an_array)
You can also use declare -p
to emit a string which can be eval
ed to get the content back:
array=( "hello world" "goodbye world" )
declare -p array
...and, on the other side...
eval "$(generate_an_array)"
However, this is less preferable -- it's not as portable to programming languages other than bash (whereas almost all languages can read a NUL-delimited stream), and it requires the receiving program to trust the sending program to return declare -p
results and not malicious content.
Upvotes: 6