在一个循环中迭代多个 Bash 数组时出现问题

在一个循环中迭代多个 Bash 数组时出现问题

我在 bash 中遇到一些数组问题。

A=( "127.0.0.1" "localhost" "aaa nnn cvcc" )
B=( "8.8.8.8"  "dns" "bbb tttt rrrr")

for n in ${A} ${B} ;  do

 if ping -c3 ${n[0]};then
  echo "${n[1]}"
    for share in ${n[2]};do
      echo $share
    done
 fi
done

我想打印数组的第二个和第三个元素,但 for 循环在 ping 处停止。这样它就起作用了。

if ping -c3 ${A[0]};then
 echo "${A[1]}"
 for share in ${A[2]};do
  echo $share
 done
fi

我确信这一定是一件非常愚蠢的事情,但这让我发疯......有什么想法吗?提前谢谢了

答案1

A您的循环实际上循环了and的第一个元素B,即${A[0]}and ${B[0]},这意味着${n[1]}and${n[2]}将是空字符串 while${n[0]}与 相同$n

bash我可能会在(版本 4.3 或更高版本)中使用名称引用变量来执行此操作:

#!/bin/bash

A=( 127.0.0.1 localhost alpha beta gamma theta )
B=( 8.8.8.8   dns       itsy bitsy 'spider man' )

for variable in A B; do
        declare -n array="$variable"

        if ping -c3 "${array[0]}" >/dev/null; then
                printf '%s\n' "${array[1]}"
                printf '\t%s\n' "${array[@]:2}"
        fi
done

我正在循环变量名字 AB,而不是它们的内容。在循环中,我创建array对名为 的变量的名称引用$variable。 Nowarray完全用作Aor B

在该if语句中,我不依赖 shell 来分割第三个数组元素以进行循环。相反,我在创建AB数组时手动拆分它,并且只需printf使用数组中的偏移量进行一次调用即可输出这些元素array。为了便于阅读,我在每个元素前面输出一个选项卡。

localhost
        alpha
        beta
        gamma
        theta
dns
        itsy
        bitsy
        spider man

if语句也可以写成

if ping -c3 "${array[0]}" >/dev/null; then
        printf '%s: ' "${array[1]}"
        ( IFS=,; printf '%s\n' "${array[*]:2}" )
fi

将其余数组元素作为冒号后的逗号分隔列表输出:

localhost: alpha,beta,gamma,theta
dns: itsy,bitsy,spider man

注:以上引用并非偶然。引用数组的扩展可确保每个元素都被单独引用。引用单个元素的扩展可确保 shell 不会在空格上拆分值(并且不会对拆分的单词进行文件名通配)。

也可以看看


您也可以在/bin/sh不使用命名数组的情况下执行此操作。相反,我们使用位置参数列表来保存数据,并用特殊单词终止该列表的每个部分(我们使用END,这是我们选择的任意字符串)。然后我们循环遍历该列表并将元素从其中移出:

#!/bin/sh

set --  127.0.0.1 localhost alpha beta gamma theta END \
        8.8.8.8   dns       itsy bitsy 'spider man' END

while [ "$#" -gt 0 ]; do
        if ping -c 3 "$1" >/dev/null; then
                printf '%s\n' "$2"
                shift 2
                while [ "$1" != END ]; do
                        printf '\t%s\n' "$1"
                        shift
                done
        else
                while [ "$1" != END ]; do shift; done
        fi

        shift
done

答案2

此答案并不直接解决您的问题,而是提供了问题的替代解决方案。它不依赖于数组。

我不会处理数组,而是使用一个文件,在我看来,该文件的优点是将数据与程序分开。更容易编写和维护。

为此,请用逗号,或其他字符分隔“共享”:

文件.txt

127.0.0.1 localhost aaa,nnn,cvcc
8.8.8.8  dns bbb,tttt,rrrr
failedip  failed a,b,c

剧本:

#!/bin/bash

# loop over the file's lines
while IFS=' ' read -r ip domain share; do
  # a blank line to separate results at each loop
  echo
  # perform the `ping`, redirect to `/dev/null` if you don't need to print the output
  if ping -c3 "$ip" &> /dev/null;then
    echo "ping to $ip successful"
    echo "domain: $domain"
    # change the IFS to comma ',' in a subshell to parse the 'shares'
    (IFS=,; printf "share: %s\n" $share)
  else
    # message if `ping` failed
    echo "ping to $ip failed"
  fi
done < file.txt

输出:


ping to 127.0.0.1 successful
domain: localhost
share: aaa
share: nnn
share: cvcc

ping to 8.8.8.8 successful
domain: dns
share: bbb
share: tttt
share: rrrr

ping to failedip failed

答案3

for n in ${A} ${B} ;  do

这不会做你想要的。获取没有索引的数组与获取索引零处的元素相同,因此${A}${A[0]}、 相同B。循环将$n一个一个地设置为各个值,因此您可以${A[0]}进行第一次迭代,然后${B[0]}进行第二次迭代。

这同样适用于${n[0]}、等${n[1]},不是数组,因此只有任何内容,其他内容只是未设置。${n[2]}$n${n[0]}

现在,Bash 并不真正处理 2D 数组,但您可以使用如下方式反转该结构:

ips=(127.0.0.1 8.8.8.8)
names=(localhost dns)
for i in "${!ips[@]}"; do
   if ping -c3 "${ips[i]}"; then
       echo "host ${names[i]-"(unknown")} is up"
   fi
done

但无论如何,您可能应该将数据存储在单独的文件中。


请注意,Zsh 中有关数据结构和循环的所有内容可能都不同,并且 IIRC ksh 也比 Bash 具有更好的数据结构支持。

相关内容