Variablen durchschleifen

16

Ich schreibe ein Bash-Skript, um rsync zu verwenden und Dateien auf etwa 20 verschiedenen Servern zu aktualisieren.

Ich habe den Rsync-Teil herausgefunden. Ich habe Probleme damit, eine Liste von Variablen durchzugehen.

Mein Skript sieht bisher so aus:

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

Wo [Server IP Address]soll der Wert der zugehörigen Variablen sein. Wenn also i = 1 ist, sollte ich den Wert von $ SERVER1 ausgeben.

Ich habe mehrere Iterationen davon ausprobiert, einschließlich

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

Es ist lange her, dass ich Skripte geschrieben habe, also weiß ich, dass mir etwas fehlt. Außerdem bin ich mir sicher, dass ich mit C # mische, was ich in den letzten 11 Jahren verwendet habe.

Ist das, was ich versuche, überhaupt möglich? Oder sollte ich diese Werte in ein Array einfügen und das Array in einer Schleife durchlaufen? Ich brauche das gleiche für Produktions-IP-Adressen sowie Standortnamen.

Dies ist alles in dem Bemühen, nicht einen Codeblock wiederholen zu müssen, den ich zum Synchronisieren der Dateien auf dem Remote-Server verwenden werde.

Paul Stoner
quelle
Vielen Dank für alle Antworten und Kommentare. Ich werde höchstwahrscheinlich den Array-Ansatz wählen.
Paul Stoner

Antworten:

33

Verwenden Sie ein Array.

#! /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
quelle
6
+1; und beachten Sie, dass Sie vor / nach / zwischen den Array-Elementen beliebige Leerzeichen einfügen können, so dass Sie (falls gewünscht) ein Element pro Zeile wie im OP einfügen können.
Ruakh
@ruakh: danke, aktualisiert, um zu zeigen, was getan werden kann.
Choroba
28

Wie die anderen Antworten zeigen, ist ein Array der bequemste Weg, dies zu tun. Der Vollständigkeit halber fragen Sie jedoch genau nach einer indirekten Erweiterung . Wie folgt umgeschrieben, funktioniert Ihr Beispiel auch mit dieser Methode:

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

Wenn Sie nur die Liste der IP-Adressen in die forSchleife einfügen möchten, können Sie auch einige geschweifte Klammern verwenden, um alles zu durchlaufen, was Sie benötigen:

#!/bin/bash

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

Wenn Sie die (möglicherweise geschweifte) Liste jedoch wiederverwenden müssen, ist die Verwendung der Liste zum Initialisieren eines Arrays der richtige Weg:

#!/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
Digitales Trauma
quelle
14

Während ich wahrscheinlich selbst mit einer der Array-Antworten gehen würde, möchte ich darauf hinweisen, dass es möglich ist, Namen direkt zu durchlaufen. Du kannst tun

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

oder ab 4.3 können Sie Folgendes verwenden nameref:

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

Hutspitze ilkkachu für die <4,3 Lösung.

Kojiro
quelle
2
Indirekte Erweiterung ( ${!var}) funktioniert auch mit älteren Versionen von Bash:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu
@ilkkachu danke für die Hilfe. Ich habe meine Antwort aktualisiert.
Kojiro
6

Jeder, der sagte, Sie sollten ein Array verwenden, ist richtig, aber - als akademische Übung - wenn Sie entschlossen wären, dies mit separaten Variablen zu tun, die mit einer Zahl enden (SERVER1, SERVER2 usw.), würden Sie dies tun es:

for ((i=1; i<7; i++))
do
    eval echo \"\$SERVER$i\"
done
Beam Davis
quelle
evalist schwer sicher zu bedienen. Ja, das kann funktionieren, aber eine indirekte Erweiterung ist im Allgemeinen besser.
Peter Cordes
Gewohnheit. Als ich anfing, Shell-Skripte zu schreiben, gab es auch in Bash keine indirekte Erweiterung. "eval" ist absolut sicher, wenn Sie verstehen, wie man richtig entkommt. Trotzdem stimme ich Ihnen zu, dass indirekte Expansion besser ist ... aber die alten Methoden funktionieren immer noch. Das würde ich auch in diesem Fall nicht tun. Ich würde ein Array verwenden!
Beam Davis
In diesem Fall feststellen , dass Sie die doppelten Anführungszeichen nicht entging, so nach dem evalgeschieht, haben Sie echo $SERVER1nicht echo "$SERVER1". Ja sicher zu bedienen, aber sehr einfach unsicher zu bedienen oder nicht so, wie Sie es beabsichtigt haben!
Peter Cordes
Da ein Servername keine Leerzeichen enthalten würde, hätte das in diesem Fall sowieso nichts ausgemacht ... aber Sie haben Recht, die doppelten Anführungszeichen sollten maskiert werden. In Antwort behoben!
Beam Davis
Richtig, die doppelten Anführungszeichen vollständig zu entfernen (um den irreführenden Eindruck zu vermeiden, dass sie etwas schützen), wäre die andere Option. Aber auch ihnen zu entkommen ist der allgemeinere Weg, also +1.
Peter Cordes