NayabSD
NayabSD

Reputation: 1120

Busybox sh won't execute the bash script

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

Answers (3)

Sidepipe
Sidepipe

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

Socowi
Socowi

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

Kusalananda
Kusalananda

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

Related Questions