同时回显两个数组的相同索引中的值

同时回显两个数组的相同索引中的值

我有 2 个数组要在 bash 脚本中同时处理。第一个数组包含某种标签。第二个数组包含值,如下所示

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

需要的是:一个循环,它将回显 LABELS 数组 & 中的索引项,并位于 VALUES 数组中该项的对应值之前,如下所示

label1 91
label2 18
label3 7
label4 4

我猜嵌套循环不起作用,我在下面尝试过,但按语法它不起作用

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

答案1

只需使用数字索引并通过索引引用数组元素:

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

或者,如果您使用的是相对较新的(>= 版本 4)bash 或其他支持关联数组的 shell,则只需使用单个数组即可:

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

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

我将变量名称更改为小写,因为在 shell 脚本中对局部变量使用大写字母是不好的做法,因为全局环境变量按照约定大写,因此在脚本中使用大写字母可能会导致变量名称冲突。

答案2

由于两个数组具有完全相同的索引(0、1、2 和 3),因此您可以${!array[@]}迭代其中一个数组的索引(也称为键),并使用该迭代器访问两个数组中的值。

这适用于“索引数组”(即整数索引)和“关联数组”(字符串索引)。

例如

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

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

输出:

label1 91
label2 18
label3 7
labe4 4

顺便说一句,您还可以使用这样的循环来填充关联数组,这在您无法同时读取键和值的情况下非常有用,就像两个手动定义的数组一样:

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

输出:

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

从现在开始,您的脚本可以使用关联数组$LV直接访问键中的值,例如

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

您还可以使用 C 风格的 for 循环,如 @terdon 和 @NickMatteo 的答案(从 0 循环到数组的长度),但这仅在数组索引为数字且连续且数组中没有间隙(未定义的索引)时才有效。

在许多/大多数情况下,这很好,因为数组经常用连续的索引号定义,但在其他情况下它不会按预期工作 - 例如,如果$array为 1、3、5、7、11、13、17 定义了索引,则将${#array[@]}返回 7,这样的循环将从 0 开始迭代。 .6(或者 0..7,如果您使用<=而不是<作为测试条件)而不是数组中的实际索引列表。

例如:

$ 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

只需循环索引即可。例如

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

除了echo,您还可以使用printf更多格式控制,例如

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

排列最多 6 个字母和最多 3 位数字的标签。

答案4

对于那些不必使用bash, 并zsh使用其${arrayA:^arrayB} 数组压缩运算符的人:

$ 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

尽管你甚至不需要循环:

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

print他们在柱子上r画了十字。a2 C

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

不过,在这里你也可以这样做:

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

对于print这两个数组,r2 Columns 上(此处省略-a)。

请注意,您可以使用这两个数组创建一个关联数组(这听起来在这里更有意义):

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

如果标签或值可能包含空字符串,您宁愿使用:

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

来保护它们。

进而:

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

为了方便起见print k,请在列上交叉,或保留空键/值:vra2 C

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

或者:

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

或者:

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

但请注意,关联数组项的扩展顺序是未指定的。

相关内容