循环遍历变量


16

我正在编写一个bash脚本,以在大约20台不同的服务器上使用rsync并更新文件。

我想通了rsync部分。我遇到的麻烦是要遍历变量列表。

到目前为止,我的脚本如下所示:

#!/bin/bash
SERVER1="192.xxx.xxx.2"
SERVER2="192.xxx.xxx.3"
SERVER3="192.xxx.xxx.4"
SERVER4="192.xxx.xxx.5"
SERVER5="192.xxx.xxx.6"
SERVER6="192.xxx.xxx.7"

for ((i=1; i<7; i++))
do
    echo [Server IP Address]
done

[Server IP Address]关联变量的值应在哪里。因此,当i = 1时,我应该回显$ SERVER1的值。

我已经尝试过几次迭代,包括

echo "$SERVER$i"    # printed the value of i
echo "SERVER$i"     # printer "SERVER" plus the value of i ex: SERVER 1 where i = 1
echo $("SERVER$i")  # produced an error SERVER1: command not found where i = 1
echo $$SERVER$i     # printed a four digit number followed by "SERVER" plus the value of i
echo \$$SERVER$i    # printed "$" plus the value of i

自编写脚本以来已经有很长时间了,所以我知道我缺少一些东西。另外,我确定我正在混合使用C#可以完成的工作,这在过去11年中一直在使用。

我要做什么甚至有可能吗?还是应该将这些值放入数组中并遍历整个数组?对于生产IP地址和位置名称,我也需要这样做。

所有这些都是为了不必重复执行将用于同步远程服务器上的文件的代码块。


感谢您的所有回复和评论。我很可能会采用数组方法。
保罗·斯通纳

Answers:


33

使用数组。

#! /bin/bash
servers=( 192.xxx.xxx.2 192.xxx.xxx.3
          192.xxx.xxx.4 192.xxx.xxx.5
          192.xxx.xxx.6 192.xxx.xxx.7
)

for server in "${servers[@]}" ; do
    echo "$server"
done

6
+1; 并请注意,您可以在数组元素之前/之后/之间放置任意空格,因此(如果需要)可以像在OP中一样,每行放置一个元素。
ruakh

@ruakh:谢谢,已更新以显示可以完成的工作。
choroba

28

正如其他答案所指出的那样,数组是执行此操作的最便捷方法。但是,为了完整起见,您要求的确切内容是间接扩展。重写如下,您的示例也可以使用此方法工作:

#!/bin/bash
SERVER1="192.xxx.xxx.2"
SERVER2="192.xxx.xxx.3"
SERVER3="192.xxx.xxx.4"
SERVER4="192.xxx.xxx.5"
SERVER5="192.xxx.xxx.6"
SERVER6="192.xxx.xxx.7"

for ((i=1; i<7; i++))
do
    servervar="SERVER$i"
    echo "${!servervar}"
done

如果您可以只将IP地址列表放入for循环中,那么您也可以考虑简单地使用一些括号扩展来迭代所需的内容:

#!/bin/bash

for server in \
192.xxx.xxx.{2..7} \
192.yyy.yyy.{42..50} \
192.zzz.zzz.254
do
    echo "$server"
done

但是,如果您需要重用(可能是花括号扩展的)列表,则可以使用该列表来初始化数组:

#!/bin/bash

servers=(
192.xxx.xxx.{2..7} 
192.yyy.yyy.{42..50}
192.zzz.zzz.254 )

for server in "${servers[@]}"
do
    echo "$server"
done

14

虽然我可能会选择一个数组来回答自己,但我想指出,可以直接遍历名称。你可以做

for name in "${!SERVER*}"; do
    echo "${!name}"
done

或者,在4.3及更高版本中,您可以使用nameref

declare -n name
for name in "${!SERVER*}"; do
    echo "$name"
done

<4.3解决方案的提示技巧ilkkachu


2
间接扩展(${!var})也适用于旧版本的Bash:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu,

@ilkkachu感谢您的协助。我已经更新了答案。
kojiro '18年

6

每个说过您应该使用数组的人都是正确的,但是-作为一项学术练习-如果您确定要使用以数字结尾的单独变量(SERVER1,SERVER2等)进行操作,这就是您的做法它:

for ((i=1; i<7; i++))
do
    eval echo \"\$SERVER$i\"
done

eval很难安全使用。是的,这是可行的,但是bash间接扩展通常是更好的方法。
彼得·科德斯

习惯。当我开始编写shell脚本时,即使是在bash中,也没有间接扩展。如果您了解如何正确逃脱,那么“ eval”是绝对安全的。不过,我同意您的观点,间接扩展会更好...但是旧方法仍然有效。在这种情况下,我也不会这样做。我会使用数组!
Beam Davis

在这种情况下,请注意您没有转义双引号,所以在eval完成后,您有echo $SERVER1,不是echo "$SERVER1"。是可以安全使用的,但是很容易以不安全的方式使用,无论您的意图如何!
彼得·科德斯

由于服务器名称不包含空格,因此在这种情况下也没关系……但是您是对的,应该将双引号转义。已修正答案!
Beam Davis

是的,另一种选择是完全删除双引号(以避免产生误导性的印象,即双引号保护着任何东西)。但是逃避它们也是更通用的方式,因此+1。
彼得·科德斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.