【问题标题】:Iterate over 2 arrays in bash - only looping over first value in each array在 bash 中迭代 2 个数组 - 仅循环每个数组中的第一个值
【发布时间】:2020-09-17 13:40:31
【问题描述】:

所以我试图在 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

输出是:-

iptables -A OUTPUT -o docker_int -p udp --dport 1111 -j ACCEPT

我希望输出是:-

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

【问题讨论】:

    标签: arrays bash loops


    【解决方案1】:

    原始代码中最直接的问题是使用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 in an online interpreter 时,作为输出发出:

    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
    

    深入探讨:为什么其他替代方案都错了

    为什么是"${!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[@]}
    

    ...为此我们see the following output:

    Using "${!array[@]}":
     - second key
     - third key
     - first key
    
    Using "${!array[*]}":
     - second key third key first key
    
    Using ${!array[*]}:
     - second
     - key
     - third
     - key
     - first
     - key
    
    Using ${!array[@]}:
     - second
     - key
     - third
     - key
     - first
     - key
    

    所以:

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

    【讨论】:

    • 数组名称是:vpn_remote_protocol 和 vpn_remote_port,而不是 vpn_remote。
    • @chepner,见编辑显示实践中的区别。
    • @chepner,就问题使用了错误的数组名称而言,这只是由于拼写错误而使其偏离主题。忽略该错误可以将问题视为主题。
    • 感谢您对此的分解,我根本没有完全理解[*][@] 的意义,因此造成混乱,我完全误以为[*] 是一些用于组合两个数组的通配符,因此数组名称为vpn_remote[*]
    • 为什么不直接从头声明数组而不是读取? ideone.com/MImBsp
    【解决方案2】:

    您应该遍历${!vpn_remote_port[*]}${!vpn_remote_protocol[*]}(不带“)

    但是这里到底发生了什么?为什么只循环每个数组中的第一个值?主要原因是数组名称错误,因此 index var 为空,它只是一个循环。因为您正在循环这个 "" 空字符串。如果我们尝试使用空的$index${vpn_remote_port[$index]} 获取数据,它将返回第一个值(索引0)。

    【讨论】:

    • 好的做法是切换到[@] 并保留引号。
    • 感谢您的回答,还按照上述评论中的建议合并了 [@]。
    • 顺便说一句,即使使用非关联数组,也可以使用未引用的${!vpn_remote_port[*]} 获得错误,只要一个具有足够奇怪的 IFS 值。请参阅 ideone.com/w9tEPO 演示此类错误。
    猜你喜欢
    • 2020-11-02
    • 2021-12-04
    • 1970-01-01
    • 2020-01-05
    • 1970-01-01
    • 2023-01-14
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多