Reputation: 1120
I have a small script called test.sh
which prints the value based on the index given.
#!/bin/sh
days_in_month=(0 31 28 31 30 31 30 31 31 30 31 30 31)
echo "${days_in_month[$1]}"
The above code works fine if I execute using bash
.
$ bash test.sh 1
31
But I need to execute the same script in an embedded board which has sh
as part of Busybox
package. When I run the command in that board, it throws an error.
$ sh test.sh
test.sh: line 2: syntax error: unexpected "("
I observe that the same error is thrown when I use dash
instead of bash
in Ubuntu system.
$ dash test.sh
test.sh: line 2: syntax error: unexpected "("
Is there any way that I could change the code so that Busybox sh will execute without any errors?
Upvotes: 2
Views: 27279
Reputation: 139
It's quite useful to use eval
in this situation. You can simply define a couple of functions:
assign_element() { eval "__ARR_$1_$2=\"$3\""; }
get_element() { eval "echo \$__ARR_$1_$2"; }
You can then do:
assign_element days_of_week 1 31
and
$ get_element days_of_week 1
31
Of course, all this is really doing is creating separate variables whose names are of a fixed format relating to the array name and element. Depending on the situation you can make those variable names more complex to avoid clashes, and there's no reason for indices to be numeric. It's also trivial to assign a list of values to numeric indices using a small script to more closely match your original question, something like:
assign_list() {
local N=0 NAME=$1
while [ -n "$2" ] ; do
assign_element "$NAME" $N "$2"
shift
N=$((N + 1))
done
}
Then your initial problem becomes:
$ assign_list days_in_month 0 31 28 31 30 31 30 31 31 30 31 30 31
$ get_element days_in_month 1
31
Upvotes: 0
Reputation: 27360
Both busybox' sh
(which isash
) and dash do not support arrays.
You could either write a big if-else or switch case statement, or use the following trick. We simulate an array using a single string with spaces as delimiters.
cut -d ' ' -f "$1" <<< "31 28 31 30 31 30 31 31 30 31 30 31"
or even more portable:
echo "31 28 31 30 31 30 31 31 30 31 30 31" | cut -d ' ' -f "$1"
Another workaround is to abuse the script's positional parameters as seen in this answer.
Upvotes: 6
Reputation: 15633
/bin/sh
in general does not support arrays, apart from the list of positional parameters.
Let's use that:
#/bin/sh
pos=$1
if [ "$pos" -lt 1 ] || [ "$pos" -gt 12 ]; then
printf 'No such month: %s\n' "$pos" >&2
exit 1
fi
set -- 31 28 31 30 31 30 31 31 30 31 30 31
shift "$(( pos - 1 ))"
printf '%s\n' "$1"
This first picks out the number from the command line and puts it into pos
. Then it sets the positional parameters that you had in your array. By shifting pos - 1
elements off this array, we have the wanted number in $1
.
This would work even if the list contained strings with spaces in them, such as in
#/bin/sh
pos=$1
if [ "$pos" -lt 1 ] || [ "$pos" -gt 12 ]; then
printf 'No such month: %s\n' "$pos" >&2
exit 1
fi
set -- "thirty one" "twenty eight" "thirty one" etc.
shift "$(( pos - 1 ))"
printf '%s\n' "$1"
The other way to solve this with /bin/sh
is with a case
statement:
case $1 in
2)
echo 28 ;;
4|6|9|11)
echo 30 ;;
1|3|5|7|8|10|12)
echo 31 ;;
*)
print 'No such month: %s\n' "$1" >&2
exit 1
esac
Upvotes: 2