Boucler les variables

16

J'écris un script bash pour utiliser rsync et mettre à jour des fichiers sur environ 20 serveurs différents.

J'ai compris la partie rsync. Ce qui me pose problème, c'est de parcourir une liste de variables.

Jusqu'à présent, mon script ressemble à ceci:

#!/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]devrait être la valeur de la variable associée. Donc, quand i = 1, je devrais faire écho à la valeur de $ SERVER1.

J'ai essayé plusieurs itérations de cela, y compris

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

Cela fait longtemps que je n'ai pas scénarisé, donc je sais que je manque quelque chose. De plus, je suis sûr que je mixe ce que je pourrais faire en utilisant C #, que j'utilise depuis 11 ans.

Ce que j'essaie de faire est-il même possible? Ou devrais-je mettre ces valeurs dans un tableau et boucler à travers le tableau? J'ai besoin de cette même chose pour les adresses IP de production ainsi que les noms d'emplacement.

Tout cela dans un effort pour ne pas avoir à répéter un bloc de code que j'utiliserai pour synchroniser les fichiers sur le serveur distant.

Paul Stoner
la source
Merci pour toutes les réponses et commentaires. Je vais plus que probablement adopter l'approche par tableau.
Paul Stoner

Réponses:

33

Utilisez un tableau.

#! /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
choroba
la source
6
+1; et notez que vous pouvez mettre des espaces arbitraires avant / après / entre les éléments du tableau, vous pouvez donc (si vous le souhaitez) mettre un élément par ligne comme dans l'OP.
ruakh
@ruakh: merci, mis à jour pour montrer ce qui peut être fait.
choroba
28

Comme le soulignent les autres réponses, un tableau est le moyen le plus pratique de le faire. Cependant, pour être complet, la chose exacte que vous demandez est une expansion indirecte . Réécrit comme suit, votre échantillon fonctionnera également en utilisant cette méthode:

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

Si vous êtes d'accord avec simplement mettre la liste des adresses IP dans la forboucle, vous pouvez également envisager simplement d'utiliser des extensions d'accolade pour répéter tout ce dont vous avez besoin:

#!/bin/bash

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

Mais si vous avez besoin de réutiliser la liste (éventuellement agrandie), alors utiliser la liste pour initialiser un tableau serait le chemin à parcourir:

#!/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
Traumatisme numérique
la source
14

Bien que j'irais probablement avec l'une des réponses du tableau moi-même, je voudrais souligner qu'il est possible de boucler directement sur les noms. Tu peux faire

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

ou, en 4.3 et plus, vous pouvez utiliser nameref:

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

Pointe du chapeau ilkkachu pour la solution <4.3.

kojiro
la source
2
L'expansion indirecte ( ${!var}) fonctionne également avec les anciennes versions de Bash:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu
@ilkkachu merci pour l'aide. J'ai mis à jour ma réponse.
kojiro
6

Tous ceux qui ont dit que vous devriez utiliser un tableau sont corrects, mais - comme exercice académique - si vous étiez déterminé à le faire avec des variables distinctes se terminant par un nombre (SERVER1, SERVER2, etc.), c'est comme ça que vous feriez il:

for ((i=1; i<7; i++))
do
    eval echo \"\$SERVER$i\"
done
Beam Davis
la source
evalest difficile à utiliser en toute sécurité. Oui, cela peut fonctionner, mais l'expansion indirecte bash est une meilleure façon en général.
Peter Cordes
Habitude. Quand j'ai commencé à écrire des scripts shell, même en bash, il n'y avait pas d'expansion indirecte. "eval" est parfaitement sûr si vous comprenez comment vous échapper correctement. Pourtant, je suis d'accord avec vous que l'expansion indirecte est meilleure ... mais les anciennes méthodes fonctionnent toujours. Je ne ferais pas non plus dans ce cas de toute façon. J'utiliserais un tableau!
Beam Davis
Dans ce cas, notez que vous n'avez pas échappé aux guillemets doubles, donc une fois evalterminé, vous l'avez fait echo $SERVER1, non echo "$SERVER1". Oui possible d'utiliser en toute sécurité, mais très facile à utiliser en toute sécurité ou pas comme vous le vouliez!
Peter Cordes
Puisqu'un nom de serveur ne contiendrait aucun espace, cela n'aurait de toute façon pas d'importance dans ce cas ... mais vous avez raison, les guillemets doivent être échappés. Corrigé en réponse!
Beam Davis
À droite, supprimer complètement les guillemets doubles (pour éviter l'impression trompeuse qu'ils protégeaient quoi que ce soit) serait l'autre option. Mais les échapper est aussi le moyen le plus générique, donc +1.
Peter Cordes