2

I have 2 arrays to prcoess in bash script simultaneously. First array contains sort of lables. Second array contains values, as under

LABELS=(label1 label2 label3 labe4 )
VALUES=(91 18 7 4)

What's required is: a loop that will echo the indexed-item from LABELS array & and in front of that corresponding value for that item from VALUES array, as under

label1 91
label2 18
label3 7
label4 4

I guess nested loop will not work, I tried below, but it won't work by syntax

for label in {LABELS[@]} && value in {VALUES[@]}
do
    echo ${label} ${value}
done
0

5 Answers 5

4

Just use a numerical index and reference the array elements by their index:

labels=(label1 label2 label3 label4)
values=(91 18 7 4)


for((i=0; i<"${#labels[@]}"; i++ )); do
  printf '%s: %s\n' "${labels[i]}" "${values[i]}"
done

Or, if you're using a relatively recent (>= version 4) bash or another shell that supports associative arrays, just use a single array instead:

declare -A values=( ["label1"]=91 ["label2"]=18 \
                    ["label3"]=7 ["label4"]=4 )

for label in "${!values[@]}"; do
  printf '%s: %s\n' "$label" "${values[$label]}"
done

I changed your variable names to lower case because it's bad practice to use CAPS for local variables in shell scripts since global environment variables are capitalized by convention, so using caps in your scripts can lead to variable name collisions.

1
  • +1. Also, you can loop directly over the indices/keys of an array with ${!name[@]} - e.g. for i in "${!labels[(@]}"; do ... ; done, without needing a C-style for loop to iterate from 0 to the array length (which can fail if the array has gaps - e.g. indexes 1 & 3 exist but 2 does not). Commented Apr 9, 2022 at 1:44
4

Since both your arrays have exactly the same indices (0, 1, 2, and 3), you can use ${!array[@]} to iterate over the indices (AKA keys) of one of the arrays and use that iterator to access the values in both arrays.

This works with both "indexed arrays" (i.e. with integer indices) and "associative arrays" (string indices).

e.g.

LABELS=(label1 label2 label3 labe4 )
VALUES=(91 18 7 4)

for i in "${!LABELS[@]}"; do
  echo "${LABELS[i]}" "${VALUES[i]}"
done

Output:

label1 91
label2 18
label3 7
labe4 4

BTW, you can also use a loop like this to populate an associative array, which can be useful in situations where you can't just read the key and the value at the same time, as with your two manually defined arrays:

LABELS=(label1 label2 label3 labe4)
VALUES=(91 18 7 4)

declare -A LV # declare LV to be an associative array

for i in "${!LABELS[@]}"; do
  LV["${LABELS[$i]}"]="${VALUES[$i]}";
done

declare -p LV

Output:

declare -A LV=([labe4]="4" [label3]="7" [label2]="18" [label1]="91" )

From now on, your script can use associative array $LV to directly access the value from the key, e.g.

$ echo "${LV[label1]}"
91

You can also use a C-style for loop as in @terdon's and @NickMatteo's answers (looping from 0 to the array's length), but that only works if the array indices are numeric and consecutive with no gaps (undefined indices) in the array.

In many/most cases, this is fine because arrays often are defined with consecutive index numbers, but in other cases it won't work as expected - e.g. if $array has defined indices for 1, 3, 5, 7, 11, 13, 17 then ${#array[@]} will return 7 and such a loop will iterate from 0..6 (or 0..7 if you use <= rather than < as the test condition) rather than over the list of actual indices in the array.

For example:

$ for i in 1 3 5 7 11 13 17 ; do let array[$i]=$i*$i ; done

$ declare -p array
declare -a array=([1]="1" [3]="9" [5]="25" [7]="49" [11]="121" [13]="169" [17]="289")

$ echo "${#array[@]}"
7

$ for ((i=0; i<"${#array[@]}"; i++)); do echo $i: "${array[$i]}" ; done
0: 
1: 1
2: 
3: 9
4: 
5: 25
6: 

$ echo "${!array[@]}"
1 3 5 7 11 13 17

$ for i in "${!array[@]}"; do echo $i: "${array[$i]}"; done
1: 1
3: 9
5: 25
7: 49
11: 121
13: 169
17: 289
3

Just loop over the indices. e.g.

for (( i = 0; i < "${#LABELS[@]}"; i++ ))
do echo "${LABELS[$i]} ${VALUES[$i]}"
done

Instead of echo, you can use printf for more format control, e.g.

printf '%6s: %3d\n' "${LABELS[$i]}" "${VALUES[$i]}"

to line up labels with up to 6 letters and numbers with up to 3 digits.

1
  • @terdon quotes inside arithmetic expressions are not necessary and actually not accepted in some shells such as zsh or mksh. Commented Apr 9, 2022 at 8:06
3

Copying one of the arrays over to the set of positional parameters and then using $1 in the loop to access the first of these. With shift, the first positional parameter is shifted off the list, and what was previously the second element becomes the first.

LABELS=(label1 label2 label3 labe4 )
VALUES=(91 18 7 4)

set -- "${VALUES[@]}"

for label in "${LABELS[@]}"; do
    printf '%s\t%s\n' "$label" "$1"
    shift
done

This avoids using indexing to get at the array elements inside the loop.

Output (tab-delimited):

label1  91
label2  18
label3  7
labe4   4
3

For those who don't have to use bash, with zsh using its ${arrayA:^arrayB} array zipping operator:

$ labels=(label1 label2 label3 labe4 )
$ values=(91 18 7 4)
$ for k v (${labels:^values}) print -r -- "$k => $v"
label1 => 91
label2 => 18
label3 => 7
labe4 => 4

Though you don't even need the loop:

$ print -raC2 -- ${labels:^values}
label1  91
label2  18
label3  7
labe4   4

prints them raw across on 2 Columns.

$ printf '%6s => %d\n' ${labels:^values}
label1 => 91
label2 => 18
label3 => 7
 labe4 => 4

Though, here you could also do:

$ print -rC2 -- $labels $values
label1  91
label2  18
label3  7
labe4   4

To print those two arrays raw on 2 Columns (leaving out -a here).

Note that you can make an associative array out of those two arrays (which sounds like it would make more sense here) with:

typeset -A assoc
assoc=( ${labels:^values} )

If labels or values may include the empty string though, you'd rather use:

assoc=( "${(@)labels:^values}" )

to preserve them.

And then:

print -raC2 -- ${(kv)assoc}

To print keys and values raw, across on 2 Columns, or to preserve empty keys/values:

print -raC2 -- "${(@kv)assoc}"

Or:

for key value ("${(@kv)assoc}") print -r -- "$key => value"

Or:

for key ("${(@k)assoc}") print -r -- "$key => $assoc[$key]"

Beware however that the order in which associative array items are expanded is unspecified.

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.