19

I want to use arrays in my sh script.

My target is to create an array with the values a b c and print all values in the array.

I succeeded to print each array, but I failed to print all values in the array.

Following example:

Set each value in arr:

n=1
eval arr$n=a
n=2
eval arr$n=b
n=3
eval arr$n=c

Print each value from arr:

n=1
eval echo \$arr$n
a
n=2
eval echo \$arr$n
b
n=3
eval echo \$arr$n
c

Now I want to print all values in $arr and instead of a b c I get:

n="*"
eval echo \$arr$n
{*}*

The values should be a b c.

2
  • check this question unix.stackexchange.com/questions/137566/…
    – Nischay
    Commented Jul 3, 2014 at 7:51
  • When you are limited to /bin/sh try avoiding too much scripting. How much colleagues will understand your eval-calls? You do want to go on vacation some day.
    – Walter A
    Commented Jul 3, 2014 at 13:43

3 Answers 3

48

Little late, but I don't see the ideal sh answer here so I'll chime in. If you don't need subscripting, then sh effectively does support arrays. It just supports them as space separated strings. You can print the entire contents of them, "push" to them, or iterate through them just fine.

Here's a bit of sample code:

NAMES=""
NAMES="${NAMES} MYNAME"
NAMES="${NAMES} YOURNAME"
NAMES="${NAMES} THEIRNAME"

echo 'One at a time...'
for NAME in ${NAMES}; do
    echo ${NAME};
done

echo 'All together now!'
echo ${NAMES}

Which outputs:

One at a time...
MYNAME
YOURNAME
THEIRNAME
All together now!
MYNAME YOURNAME THEIRNAME

Now, I said it doesn't support sub-scripting, but with a little bit of cut magic and using the space as a proper delimiter, you can absolutely emulate that. If we add this to the bottom of our above example:

echo 'Get the second one'
echo ${NAMES} | cut -d' ' -f2

echo 'Add one more...'
NAMES="${NAMES} TOM"

echo 'Grab the third one'
echo ${NAMES} | cut -d' ' -f3

And run it, we get:

Get the second one
YOURNAME
Add one more...
Grab the third one
THEIRNAME

Which is what we'd expect!

However, a string with a space in it can cause an issue and will completely break the sub-scripting.

So, really the better statement is: Arrays are non-obvious in sh and handling arrays of strings with spaces is hard. If you don't need to do that (e.g., an array of host names you want to deploy to), then sh is still a fine tool for the job.

2
  • Wasn't able to reproduce the cut part as each elements are thrown in a new line instead of a space when echoing the variable
    – Zulgrib
    Commented May 24, 2017 at 22:26
  • That my be due to the shell you're using. Can you share your script (shebang included) and shell version?
    – hjc1710
    Commented May 25, 2017 at 23:03
11

sh does not support array, and your code does not create an array. It created three variable arr1, arr2, arr3.

To initialize an array element in a ksh-like shell, you must use syntax array[index]=value. To get all element in array, use ${array[*]} or ${array[@]}.

Try:

n=1
eval arr[$n]=a
n=2
eval arr[$n]=b
n=3
eval arr[$n]=c

n=1
eval echo \${arr[$n]}
n=2
eval echo \${arr[$n]}
n=3
eval echo \${arr[$n]}

n='*'

eval echo \${arr[$n]}
6
  • 1
    I must to use sh script ) I dont have ksh/bash ) on my box machine , so ksh isnt relevant sorry Commented Jul 3, 2014 at 7:54
  • 1
    sh does not support array!
    – cuonglm
    Commented Jul 3, 2014 at 7:55
  • Is doing eval echo \${arr[$n]} not the same as doing ${arr[$n]} ?
    – Emmanuel
    Commented Jul 3, 2014 at 7:57
  • It's different. echo ${arr[$n]} causes syntax error.
    – cuonglm
    Commented Jul 3, 2014 at 8:04
  • 1
    But you can use arr[$n]=a without eval. Commented Jul 3, 2014 at 16:24
0

I agree that the correct answer is to find a different approach.  Get a better shell, switch to some other scripting language (maybe perl or python), or rethink your design.  But, to get a list of the values of all the variables whose names begin with arr, you can use

set | sed -n '/^arr[^=]*=/s///p'

This will list them on separate lines.  To get them all on one line, use

echo $(set | sed -n '/^arr[^=]*=/s///p')

Not that this will sort them in lexicographical order, so arr10arr19 will appear between arr1 and arr2.  (The same thing happens if you have files named maih1, maih2, …, maih10, …, maih19.)  If you know in advance how many array elements you are going to have, you can fix this by using leading zeroes; e.g., arr01. arr02, …, will get you up to 99.

P.S. The trick with set | sed … will fail if any of the variables’ values contain newline(s).

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.