Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return an array in bash without using globals?

I have a function that creates an array and I want to return the array to the caller:

create_array() {   local my_list=("a", "b", "c")   echo "${my_list[@]}" }  my_algorithm() {   local result=$(create_array) } 

With this, I only get an expanded string. How can I "return" my_list without using anything global?

like image 907
helpermethod Avatar asked May 14 '12 11:05

helpermethod


People also ask

How do I print an array in bash?

Print Bash Array We can use the keyword 'declare' with a '-p' option to print all the elements of a Bash Array with all the indexes and details. The syntax to print the Bash Array can be defined as: declare -p ARRAY_NAME.

What is $@ in bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.

Can bash function return multiple values?

Yes, bash 's return can only return numbers, and only integers between 0 and 255.


2 Answers

With Bash version 4.3 and above, you can make use of a nameref so that the caller can pass in the array name and the callee can use a nameref to populate the named array, indirectly.

#!/usr/bin/env bash  create_array() {     local -n arr=$1             # use nameref for indirection     arr=(one "two three" four) }  use_array() {     local my_array     create_array my_array       # call function to populate the array     echo "inside use_array"     declare -p my_array         # test the array }  use_array                       # call the main function 

Produces the output:

inside use_array declare -a my_array=([0]="one" [1]="two three" [2]="four") 

You could make the function update an existing array as well:

update_array() {     local -n arr=$1             # use nameref for indirection     arr+=("two three" four)     # update the array }  use_array() {     local my_array=(one)     update_array my_array       # call function to update the array } 

This is a more elegant and efficient approach since we don't need command substitution $() to grab the standard output of the function being called. It also helps if the function were to return more than one output - we can simply use as many namerefs as the number of outputs.


Here is what the Bash Manual says about nameref:

A variable can be assigned the nameref attribute using the -n option to the declare or local builtin commands (see Bash Builtins) to create a nameref, or a reference to another variable. This allows variables to be manipulated indirectly. Whenever the nameref variable is referenced, assigned to, unset, or has its attributes modified (other than using or changing the nameref attribute itself), the operation is actually performed on the variable specified by the nameref variable’s value. A nameref is commonly used within shell functions to refer to a variable whose name is passed as an argument to the function. For instance, if a variable name is passed to a shell function as its first argument, running

declare -n ref=$1 inside the function creates a nameref variable ref whose value is the variable name passed as the first argument. References and assignments to ref, and changes to its attributes, are treated as references, assignments, and attribute modifications to the variable whose name was passed as $1.

like image 172
codeforester Avatar answered Sep 19 '22 11:09

codeforester


What's wrong with globals?

Returning arrays is really not practical. There are lots of pitfalls.

That said, here's one technique that works if it's OK that the variable have the same name:

$ f () { local a; a=(abc 'def ghi' jkl); declare -p a; } $ g () { local a; eval $(f); declare -p a; } $ f; declare -p a; echo; g; declare -p a declare -a a='([0]="abc" [1]="def ghi" [2]="jkl")' -bash: declare: a: not found  declare -a a='([0]="abc" [1]="def ghi" [2]="jkl")' -bash: declare: a: not found 

The declare -p commands (except for the one in f() are used to display the state of the array for demonstration purposes. In f() it's used as the mechanism to return the array.

If you need the array to have a different name, you can do something like this:

$ g () { local b r; r=$(f); r="declare -a b=${r#*=}"; eval "$r"; declare -p a; declare -p b; } $ f; declare -p a; echo; g; declare -p a declare -a a='([0]="abc" [1]="def ghi" [2]="jkl")' -bash: declare: a: not found  -bash: declare: a: not found declare -a b='([0]="abc" [1]="def ghi" [2]="jkl")' -bash: declare: a: not found 
like image 23
Dennis Williamson Avatar answered Sep 20 '22 11:09

Dennis Williamson