littlerunaway
littlerunaway

Reputation: 453

how to return an array from a script in Bash?

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

Answers (3)

rici
rici

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:

  1. Any function or script does have a status return, which is a small integer; this is what is actually returned by the return and exit special forms. However, the status return mostly works as a boolean value, and certainly cannot carry a structured value.

Upvotes: 3

glenn jackman
glenn jackman

Reputation: 247210

hello.sh

declare -a array       # declares a global array variable
array=(
    "hello world"
    "goodbye world"
)

other.sh

. 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:

hello.sh

#!/bin/bash
array=(
    "hello world"
    "goodbye world"
)
printf "%s\n" "${array[@]}"

other.sh

#!/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

Charles Duffy
Charles Duffy

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 evaled 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

Related Questions