How to port to bash-style arrays to ash?

12,632

Solution 1

Before arrays were in bash, ksh, and other shells, the usual method was to pick a delimiter that wasn't in any of the elements (or one that was uncommon to minimise any required escaping), and iterate over a string containing all the elements, separated by that delimiter. Whitespace is usually the most convenient delimiter choice because the shell already splits "words" by whitespace by default (you can set IFS if you want it to split on something different).

For example:

# backslash-escape any non-delimiter whitespace and all other characters that
# have special meaning to the shell, e.g. globs, parenthesis, ampersands, etc.
services='service1.service service2.service service3.service'

for s in $services ; do  # NOTE: do not double-quote $services here.
  START "$s"
done

$services should NOT be double-quoted here because we want the shell to split it into "words".

Solution 2

ash does not have arrays. The only thing that comes close is the positional parameters, so you could do

set -- "service1.service" \
       "service2.service" \
       "service3.service"

for service in "$@"
do
   START $service
done

Solution 3

If you need to refer to the list of services only once, you can use a here-doc:

while IFS= read -r service
do
   START "$service"
done << END
service1.service
service2.service
service3.service
END

Note that the service names should not be quoted in the list (although "$service" probably should be quoted, unless you have a good reason not to).  If you would like to have the service names indented, use <<- instead of << and indent the names with tabs:

while IFS= read -r service
do
   START "$service"
done <<- END
        service1.service
        service2.service
        service3.service
END
Share:
12,632

Related videos on Youtube

metamorphling
Author by

metamorphling

Making gamedev in Japan great again

Updated on September 18, 2022

Comments

  • metamorphling
    metamorphling almost 2 years

    Some time ago I have written a bash script which now should be able to run in environment with ash.

    In bash it was like :

    services=( "service1.service"
               "service2.service"                                       
               "service3.service" )  
    
    for service in "${services[@]}"
    do
       START $service                   
    done
    
    START()
    {
       echo "Starting "$1
       systemctl start $1
    }
    

    In reality there are like 40 services in array, and I want to make this transition as painless and clean as possible. Have always been using bashisms. Now I'm in a pinch with the task to make scripts more portable.

    For portability reasons probably it would be nice to have a pure ash solution. But since I have a pretty robust busybox at my disposal I might sacrifice some portability. Only if readability improves a lot, since "clean" script is a metric too.

    What would be portable and clean solution in this case?