2

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?

3
  • Can you make Hello a function rather than a separate script? Commented Aug 13, 2014 at 16:26
  • 1
    well, I probably will have to use it in 2 different scripts, so instead of writing the function twice I rather use a script. Commented Aug 13, 2014 at 16:28
  • @littlerunaway, there's middle ground between those -- creating a library of shared functions, and sourcing it into both scripts. Commented Aug 13, 2014 at 16:52

3 Answers 3

6

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.

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

3 Comments

well, since we haven't seen pretty much any of these in class, I don't think I can use it. but I think I found a way around my problem
@littlerunaway, your class won't let you learn anything from outside sources? That's... silly; everything used in this answer is included in the bash manual, and I'd hope your class would encourage you to use that as a reference.
bash is a relatively small part of the course. I guess they don't want us to get too deep into it at this point, and that everything we did see in class should be enough
3

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.

1 Comment

+1. Might mention namevars as a technique for more interesting cases (in bash 4.3+ or ksh93 where they're available).
1

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]}"

1 Comment

Ought to at least make the caveat (that the latter code doesn't work with array values containing literal newlines) explicit.

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.