'iterating a list of strings with whitespace in POSIX sh

I have two lists of strings (with whitespace in each string), and want to read them one by one. The length of the strings are the same, so I get the length of one of them, via "${!argument[@]}", and try to read the elements of the lists. But it fails:

arguments="--a 100 --b 200" "--a 100 --b 200 --c"
settings="without_c" "with_c"
for index in "${!argument[@]}"
do
        setting=${setting[$index]}
        argument=${argument[$index]}
done

Gives the following error:

alp@ubuntu:~$ sh toy.sh 
toy.sh: 1: toy.sh: --a 100 --b 200 --c: not found
toy.sh: 2: toy.sh: with_c: not found
toy.sh: 3: toy.sh: Bad substitution
sh


Solution 1:[1]

Your first line reads

arguments="--a 100 --b 200" "--a 100 --b 200 --c"

This is a special case of the following structure:

V=X P

(with V being arguments, X being "--a 100 --b 200" and P being --a 100 --b 200 --c).

The semantic of such a statement is to execute the program P in an environment, where the environment variable V is set to X.

In your case, it means that you ask the shell to execute the "program" --a 100 --b 200 --c and such a program does not exist. This is what the error message says.

In your title, you say that you want to process a list, but you are using a programming language (Posix shell), which does not support lists. You are not using bash, as you claim in your comment, because if you would use bash, the error message would be different.

Of course, even in bash, the first line would be incorrect, because an array assignment (what you call a list is called an array in bash) in bash would follow the syntax name=(val1 val2 ....).

Solution 2:[2]

I like to do "mock" 2 dimensional arrays in POSIX sh (/bin/sh) using something like the following. Note, you'll need to pick a delimiter (in this case |) that is not used in your strings.

#!/bin/sh
## Save this script as testloop.sh
for STRING in "red|little corvette" "green|hip, funky onions" "blue|high, high moon"
do
    IFS='|' read -r COLOUR THING <<-_EOF_

$STRING
_EOF_

    echo "The colour of the $THING is $COLOUR"
done

When you run that, you get:

sh testloop.sh
The colour of the little corvette is red
The colour of the hip, funky onions is green
The colour of the high, high moon is blue

For your example, you would use something similar

#!/bin/sh
## Save this script as loopArguments.sh

for STRING in "--a 100 --b 200|without_c|1st" "--a 100 --b 200 --c|with_c|2nd" "--a 300 --b 400|without_c|3rd"
do

    IFS='|' read -r LINE CSTATE NUMBER <<-_EOF_
$STRING
_EOF_

    echo "The $NUMBER line is $LINE and it is $CSTATE"
done

which will do this:

sh loopArguments.sh
The 1st line is --a 100 --b 200 and it is without_c
The 2nd line is --a 100 --b 200 --c and it is with_c
The 3rd line is --a 300 --b 400 and it is without_c

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 user1934428
Solution 2