Loop através de variáveis


16

Estou escrevendo um script bash para usar o rsync e atualizar arquivos em cerca de 20 servidores diferentes.

Eu tenho a parte do rsync descoberta. O que eu estou tendo problemas é através de uma lista de variáveis.

Até agora, meu script é assim:

#!/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

Onde [Server IP Address]deve ser o valor da variável associada. Então, quando i = 1, devo ecoar o valor de $ SERVER1.

Eu tentei várias iterações disso, incluindo

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

Faz muito tempo desde que eu escrevi o script, então sei que estou perdendo alguma coisa. Além disso, tenho certeza de que estou misturando o que poderia fazer usando C #, que usei nos últimos 11 anos.

O que estou tentando fazer é possível? Ou devo colocar esses valores em uma matriz e fazer um loop pela matriz? Preciso fazer o mesmo para endereços IP de produção e nomes de locais.

Tudo isso é um esforço para não precisar repetir um bloco de código que usarei para sincronizar os arquivos no servidor remoto.


Obrigado por todas as respostas e comentários. Provavelmente, adotarei a abordagem de matriz.
— Paul Stoner

Respostas:


33

Use uma matriz.

#! /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; e observe que você pode colocar um espaço em branco arbitrário antes / depois / entre os elementos da matriz, para que você possa (se desejar) colocar um elemento por linha como no OP.
— Ruakh 31/07

@ruakh: obrigado, atualizado para mostrar o que pode ser feito.
— choroba 31/07

28

Como as outras respostas apontam, uma matriz é a maneira mais conveniente de fazer isso. No entanto, para ser completo, o que você está pedindo é a expansão indireta . Reescrito da seguinte forma, sua amostra também funcionará usando este método:

#!/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

Se você concorda em colocar a lista de endereços IP no forloop, considere também o uso de algumas expansões de chaves para iterar o que você precisar:

#!/bin/bash

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

Mas se você precisar reutilizar a lista (possivelmente entre chaves), usar a lista para inicializar uma matriz seria o caminho a seguir:

#!/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

Embora eu provavelmente deva responder uma das respostas da matriz, gostaria de salientar que é possível repetir nomes diretamente. Você pode fazer

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

ou, nas versões 4.3 e superior, você pode usar um nameref:

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

Gorro de ilkkachu para a solução <4,3.


2
Expansão indireta ( ${!var}) funciona com versões mais antigas do Bash também:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
— ilkkachu

@ilkkachu obrigado pela assistência. Eu atualizei minha resposta.
— Kojiro # 01

6

Todos que disseram que você deveria usar uma matriz estão corretos, mas - como exercício acadêmico - se você estivesse determinado a fazê-lo com variáveis ​​separadas que terminam em um número (SERVER1, SERVER2, etc.), é assim que você faria isto:

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

evalé difícil de usar com segurança. Sim, isso pode funcionar, mas bash expansão indireta é uma maneira melhor em geral.
— Peter Cordes

Hábito. Quando comecei a escrever scripts de shell, mesmo no bash, não havia expansão indireta. "eval" é perfeitamente seguro se você entender como escapar corretamente. Ainda assim, concordo com você que a expansão indireta é melhor ... mas os métodos antigos ainda funcionam. Eu também não faria neste caso. Eu usaria uma matriz!
— Davis

Nesse caso, observe que você não escapou das aspas duplas; portanto, depois que evalterminar, você echo $SERVER1não conseguiu echo "$SERVER1". Sim, é possível usar com segurança, mas é muito fácil de usar de maneira insegura ou não, como você pretendia!
— Peter Cordes

Como um nome de servidor não conteria espaços, nesse caso não teria importância ... mas você está certo, as aspas duplas devem ser escapadas. Corrigido em resposta!
— Davis

Certo, remover completamente as aspas duplas (para evitar a impressão enganosa de que estavam protegendo alguma coisa) seria a outra opção. Mas escapar deles também é a maneira mais genérica, então +1.
— 22818 Peter Cordes
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.