在 bash 中迭代 2 个数组 - 仅循环每个数组中的第一个值

bin*_*hex 1 arrays bash loops

所以我试图在 bash 中循环遍历两个数组,但我正在努力让它工作,它似乎在第一次迭代时停止并且没有循环整个数组,请注意我正在使用 IFS,因为我想定义分隔符.

#!/bin/bash

protocol="udp,tcp-client,udp,"
port="1111,2222,3333,"

# split comma separated string into list from VPN_REMOTE_PROTCOL variable
IFS=',' read -a vpn_remote_protocol <<< "${protocol}"

# split comma separated string into list from VPN_REMOTE_PORT variable
IFS=',' read -a vpn_remote_port <<< "${port}"

for index in "${!vpn_remote[*]}"; do

        echo "iptables -A OUTPUT -o docker_int -p ${vpn_remote_protocol[$index]} --dport ${vpn_remote_port[$index]} -j ACCEPT"

done
Run Code Online (Sandbox Code Playgroud)

输出是:-

iptables -A OUTPUT -o docker_int -p udp --dport 1111 -j ACCEPT
Run Code Online (Sandbox Code Playgroud)

我希望输出是:-

iptables -A OUTPUT -o docker_int -p udp --dport 1111 -j ACCEPT
iptables -A OUTPUT -o docker_int -p tcp-client --dport 2222 -j ACCEPT
iptables -A OUTPUT -o docker_int -p udp --dport 3333 -j ACCEPT
Run Code Online (Sandbox Code Playgroud)

Cha*_*ffy 5

最直接的原代码的问题是使用vpn_remote作为数组的名字,我们正在寻找密钥,其中代替vpn_remote_protocolvpn_remote_port。的下一个问题是使用的"${!array[*]}",其产生单串级联的所有键一起; 就像for item in "two words"只运行一次two words作为分配给项目的值,在"${!vpn_remote_port[*]}"这里循环只会运行for index in... 一次"0 1 2"作为项目。(相比之下,如果变量名不正确,"""${!array[@]}"计算结果为;在被视为 的数字上下文中0,这就是您看到第一项而没有其他项的原因)。


这个故事的寓意

使用"${!array[@]}", 从不"${!array[*]}",${!array[*]}${!array[@]}不理解的差异,并有一个明确的原因。

因此:

#!/bin/bash

protocol="udp,tcp-client,udp,"
port="1111,2222,3333,"

IFS=',' read -a vpn_remote_protocol <<< "${protocol}"
IFS=',' read -a vpn_remote_port <<< "${port}"

for index in "${!vpn_remote_port[@]}"; do
    echo "iptables -A OUTPUT -o docker_int -p ${vpn_remote_protocol[$index]} --dport ${vpn_remote_port[$index]} -j ACCEPT"
done
Run Code Online (Sandbox Code Playgroud)

...当在在线解释器中运行时,它会作为输出发出:

iptables -A OUTPUT -o docker_int -p udp --dport 1111 -j ACCEPT
iptables -A OUTPUT -o docker_int -p tcp-client --dport 2222 -j ACCEPT
iptables -A OUTPUT -o docker_int -p udp --dport 3333 -j ACCEPT
Run Code Online (Sandbox Code Playgroud)

更深入:为什么其他选择都错了

为什么"${!array[@]}"?让我们比较每个的行为,但在一个更具挑战性的情况下,当我们的键不是纯数字时(尽管我们可能会通过使用不正确的方法即使使用纯数字键也会产生错误,如果我们的IFS值中有数字):

declare -A array=(
  ["first key"]="first value"
  ["second key"]="second value"
  ["third key"]="third value"
)

printf '%s\n' 'Using "${!array[@]}":'
printf ' - %s\n' "${!array[@]}"

printf '\n%s\n' 'Using "${!array[*]}":'
printf ' - %s\n' "${!array[*]}"

printf '\n%s\n' 'Using ${!array[*]}:'
printf ' - %s\n' ${!array[*]}

printf '\n%s\n' 'Using ${!array[@]}:'
printf ' - %s\n' ${!array[@]}
Run Code Online (Sandbox Code Playgroud)

...我们看到以下输出

#!/bin/bash

protocol="udp,tcp-client,udp,"
port="1111,2222,3333,"

IFS=',' read -a vpn_remote_protocol <<< "${protocol}"
IFS=',' read -a vpn_remote_port <<< "${port}"

for index in "${!vpn_remote_port[@]}"; do
    echo "iptables -A OUTPUT -o docker_int -p ${vpn_remote_protocol[$index]} --dport ${vpn_remote_port[$index]} -j ACCEPT"
done
Run Code Online (Sandbox Code Playgroud)

所以:

  • "${!arrayname[*]}" 是错误的,因为它扩展为一个字符串,所有的键都连接在一起(第一个字符是 IFS-- 默认情况下是一个空格 -- 在它们之间)。
  • ${!arrayname[*]}${!arrayname[@]}两者都是错误的,因为键在被视为项目列表之前需要进行分词和全局扩展
  • "${!arrayname[@]}" 做正确的事情,将每个键视为一个单词,不受可能首先损坏它的操作的影响。