Returning array from a Bash function
Asked Answered
O

6

10

I am making a bash script and I have encountered a problem. So let's say I got this

function create_some_array(){
  for i in 0 1 2 3 .. 10
  do
    a[i]=$i
  done
}

create_some_array
echo ${a[*]}

Is there any way I can make this work? I have searched quite a lot and nothing I found worked. I think making the a[] a global variable should work but I can't find something that actually works in my code. Is there any way to return the array from the function to main program?

Thanks in advance

Orthicon answered 12/2, 2013 at 18:24 Comment(12)
This code works without any problems. Do not invent code to show the problem. Show us actual code that is causing the problem.Brougham
Welcome to Stack Overflow. Please improve your question by posting all relevant error messages exactly as they appear. Also, make sure to include a properly-formatted sample of your expected output so folks understand the results you're trying to achieve.Enrage
@depesz: I half-agree. I think the asker should "invent code to show the problem" -- but (s)he has to make sure that it is (as you put it) "actual code that is causing the problem". This is known as an SSCCE -- a short, self-contained, correct (compilable) example.Axon
@ruakh: well, he/she has the code that is causing the problem. It's just that his invented version for showing it - doesn't contain problems. Unfortunately it's very common (at least on IRC) that people have problem with something, but show something different.Brougham
pastebin.com/2QcWfUNE It is going to expire in 1hOrthicon
the last 10 lines of my code won't workOrthicon
"won't work" is not a technical description. Try running your script with bash -x script_name which will trace what is happening.Tintoretto
bah damn! thanks cdarke! Well there was an exit 0 that was shutting down my programm before displaying my arrays! I commented it out and it worked!! Thanks :)Orthicon
mkdir tmp; touch tmp/{file1,file2}; ln tmp/file2 tmp/file3; bash yourscript tmp outputs "The index number of .../tmp/file2 is 28578767 and its hard link is 2", "file2 is hardlinked to file3 with indexnumber: 28578767, file3 is hardlinked to file2 with indexnumber: 28578767". Seems to be working fine.Groundnut
@depesz: Right, but do you really want every question-asker to post the entire program that they have a problem with? It's better for them to narrow down the problem, and post a small-but-complete program that demonstrates the problem. This asker missed the "that demonstrates the problem" part; you want to go too far in the other direction, by missing the "small" part.Axon
It worked fine now, as I said program was shutting down before it reads the last 10± lines that's why I couldn't get the the desired output! Thanks you all for your help!Orthicon
@ruakh: I do not want to see whole program. But if I have to choose, I choose too much information vs. too small, and misleading.Brougham
B
5

This won't work as expected when there are whitespaces in the arrays:

function create_some_array() {
    local -a a=()
    for i in $(seq $1 $2); do
        a[i]="$i $[$i*$i]"
    done
    echo ${a[@]}
}

and worse: if you try to get array indices from the outside "a", it turns out to be a scalar:

echo ${!a[@]}

even assignment as an array wont help, as possible quoting is naturally removed by the echo line and evaluation order cannot be manipulated to escape quoting: try

function create_some_array() {
...
    echo "${a[@]}"
}

a=($(create_some_array 0 10))
echo ${!a[@]}

Still, printf seems not to help either:

function create_some_array() {
...
    printf " \"%s\"" "${a[@]}"
}

seems to produce correct output on one hand:

$ create_some_array 0 3; echo
 "0 0" "1 1" "2 4" "3 9"

but assignment doesn't work on the other:

$ b=($(create_some_array 0 3))
$ echo ${!b[@]}
0 1 2 3 4 5 6 7

So my last trick was to do assignment as follows:

$ eval b=("$(create_some_array 0 3)")
$ echo -e "${!b[@]}\n${b[3]}"
0 1 2 3
3 9

Tataaa!

P.S.: printf "%q " "${a[@]}" also works fine...

Beefeater answered 23/11, 2016 at 16:4 Comment(1)
annoying! this works but my IDE is given me an error that it wont suppress ... parsing stopped here. Invalid use of parantheses? .. annoying but +1Timid
G
3

This works fine as described. The most likely reason it doesn't work in your actual code is because you happen to run it in a subshell:

cat textfile | create_some_array
echo ${a[*]}

would not work, because each element in a pipeline runs in a subshell, and

myvalue=$(create_some_array)
echo ${a[*]}

would not work, since command expansion happens in a subshell.

Groundnut answered 12/2, 2013 at 18:29 Comment(1)
j=ls -i "$i" | awk '{print $1}'; inode[count]=$j; This is the part i insert into an array. And when I try to echo this outside the function it dosnt workOrthicon
A
3

You can make an array local to a function, and then return it:

function create_some_array(){
    local -a a=()
    for i in $(seq $1 $2); do
        a[i]=$i
    done
    echo ${a[@]}
}

declare -a a=()

a=$(create_some_array 0 10)

for i in ${a[@]}; do
   echo "i = " $i
done
Allodium answered 7/6, 2014 at 19:34 Comment(0)
C
3

Hi here is my solution:

show(){
    local array=()
    array+=("hi")
    array+=("everything")
    array+=("well?")
    echo "${array[@]}"
}

for e in $(show);do
    echo $e
done

Try this code on: https://www.tutorialspoint.com/execute_bash_online.php

Cyzicus answered 24/4, 2019 at 9:49 Comment(2)
this works +1 but its kinda annoying coz if u dump the length of the array it is always 1 - my IDE warns me its converted it to a string and the console output agreesTimid
Add a space to one of your array elements and you'll find this doesn't work any more.Hajj
T
0

Both these work for me with sh and bash:

arr1=("192.168.3.4" "192.168.3.4" "192.168.3.3")

strArr=$(removeDupes arr1) # strArr is a string
for item in $strArr; do arr2+=("$item"); done # convert it to an array
len2=${#arr2[@]} # get array length
echo "${len2}" # echo length

eval arr3=("$(removeDupes arr1)") # shellcheck does not like this line and won't suppress it but it works
len3=${#arr3[@]} # get array length
echo "${len3}" # echo length

As an aside, the removeDupes function looks like this:

removeDupes() {
  arg="$1[@]"
  arr=("${!arg}")
  len=${#arr[@]}
  resultArr=()
  # some array manipulation here
  echo "${resultArr[@]}"
}

This answer is based on but better explains and simplifies the answers from @Hans and @didierc

Timid answered 20/5, 2021 at 15:49 Comment(0)
C
0

Your function doesn't return an array. It creates a global array variable called $a.

function create_some_array(){
  for i in 0 1 2 3 .. 10
  do
    a[i]=$i
  done
}

To return an array:

function create_some_array(){
  local a
  for i in 0 1 2 3 .. 10; do
    a[i]=$i
  done
  printf "%s\n" "${a[@]}"
}

# capture the created array:
readarray -t my_array < <(create_some_array)

# print its elements
for element in "${my_array[@]}"; do
  echo "  - ${element}"
done

This method, doesn't just simulate a returned array, but it also respects the spaces in a particular element.

Hope it helps

Cathrine answered 26/8 at 20:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.