Ich habe eine Datei servers.txt
mit einer Liste von Servern:
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
wenn ich die datei zeilenweise mit lese while
und jede zeile wiederhole, funktioniert alles wie erwartet. Alle Zeilen werden gedruckt.
$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
Wenn ich jedoch zu allen Servern sshen und einen Befehl ausführen möchte, while
funktioniert meine Schleife plötzlich nicht mehr:
$ while read HOST ; do ssh $HOST "uname -a" ; done < servers.txt
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Dies stellt nur eine Verbindung zum ersten Server in der Liste her, nicht zu allen. Ich verstehe nicht, was hier passiert. Kann jemand bitte erklären?
Dies ist noch seltsamer, da die Verwendung der for
Schleife gut funktioniert:
$ for HOST in $(cat servers.txt ) ; do ssh $HOST "uname -a" ; done
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server2 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server3 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Es muss etwas Besonderes sein ssh
, da andere Befehle gut funktionieren, wie zum Beispiel ping
:
$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt
ssh
scripting
io-redirection
Martin Vegter
quelle
quelle
ansible
Antworten:
ssh
liest den Rest Ihrer Standardeingabe.read
liest aus stdin. Das<
leitet stdin von einer Datei um.Leider lautet der Befehl, den Sie ausführen möchten, auch "stdin", sodass der Rest Ihrer Datei verschlungen wird. Sie sehen es deutlich mit:
Beachten Sie, wie
cat
die restlichen zwei Zeilen gegessen (und wiedergegeben) wurden. (Hätte man es wie erwartet gelesen, würde jede Zeile das "Start" und "Ende" um den Host haben.)Warum
for
arbeitet?Ihre
for
Leitung leitet nicht zu stdin um. (Tatsächlich liest es den gesamten Inhalt derservers.txt
Datei vor der ersten Iteration in den Speicher.) Liest alsossh
weiterhin seinen stdin vom Terminal (oder möglicherweise nichts, je nachdem, wie Ihr Skript aufgerufen wird).Lösung
Zumindest in Bash können Sie
read
einen anderen Dateideskriptor verwenden.sollte funktionieren.
10
ist nur eine beliebige Dateinummer, die ich ausgewählt habe. 0, 1 und 2 haben definierte Bedeutungen, und das Öffnen von Dateien beginnt normalerweise mit der ersten verfügbaren Nummer (daher wird als Nächstes 3 verwendet). 10 ist also hoch genug, um aus dem Weg zu gehen, aber niedrig genug, um in einigen Shells unter der Grenze zu sein. Plus es ist eine schöne runde Zahl ...Alternative Lösung 1: -n
Wie McNisse in seiner Antwort betont , hat der OpenSSH-Client eine
-n
Option, die verhindert, dass er Standardinhalte liest. Dies funktioniert in bestimmten Fällen gutssh
, aber natürlich kann dies bei anderen Befehlen fehlen - die anderen Lösungen funktionieren, unabhängig davon, welcher Befehl Ihren Standardwert aufnimmt.Alternative Lösung 2: Zweite Umleitung
Sie können anscheinend (wie in, ich habe es versucht, es funktioniert zumindest in meiner Version von Bash ...) eine zweite Umleitung durchführen, die ungefähr so aussieht:
Sie können dies mit jedem Befehl verwenden, aber es wird schwierig, wenn Sie tatsächlich eine Terminal-Eingabe für den Befehl wünschen.
quelle
10
nummerexec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&-
Dadurch wird aus dem Dateideskriptor 3 eine Sicherungskopie der ursprünglichen Standard-ID erstellt, die dann für die Standard-ID von ssh verwendet wird. Anschließend wird die FD-Sicherungskopie geschlossen, wenn Sie fertig sind. Dies funktioniert auf jeder POSIX-kompatiblen Shell.-u
Option wird von POSIX nicht unterstützt und sollte daher nicht für#!/bin/sh
Skripte verwendet werden. Verwenden Sieread HOST <&10
stattdessen. Außerdem benötigt POSIX nur Shells, um die Dateideskriptoren 0 bis 9 zu unterstützen, und10<servers.txt
kann daher nicht verwendet werden, wenn das Skript streng konform sein soll.Wie derobert beschreibt
ssh
liest deinstdin
.Um dieses Verhalten zu ändern, können Sie -n no ssh hinzufügen, um zu verhindern, dass stdin gelesen wird.
quelle
Sie wären wahrscheinlich besser dran, wenn Sie pssh aus dem parallel-ssh- Projekt verwenden.
-i
bedeutet interaktiv. pssh kommt auch mit einem parallelen scp und einem parallelen rsync. Das Schöne ist, dass es asynchron läuft und so viele Threads ausführt, wie Sie es verlangen. Die Standardeinstellung (nicht -i / interactive) ist die Ausgabe in separate Verzeichnisse für stdout / stderr. Dies erfolgt über $ outputdir / $ hostname.quelle
Wenn Sie diese Art von Aufgabe häufig erledigen, sollten Sie Fabric ausprobieren
Installieren Sie den Stoff gemäß den Anweisungen , die Sie in den meisten Fällen nur benötigen
sudo apt-get install fabric
Erstellen Sie eine Datei
fabfile.py
mit folgendem Namen :Dann führen
fab mytask
Sie das gewünschte Ergebnis aus.quelle
Es ist einfacher, einen Befehl wie den folgenden zu verwenden:
Normalerweise mag ich das:
Das
echo
ist, um zu sehen, welcher Server feststeckt oder keine Verbindung herstellen kann.quelle
Weil ein ssh-Befehl den gesamten Stream von der Standardeingabe nimmt, gespeist von der while-Anweisung,
Sie können eine Pipe verwenden, um das stdin von ssh auf eine andere Quelle umzuschalten:
Beispiel:
Die Standardeingabe aller ssh-Befehle in einer while-Schleife muss auf eine andere Quelle umgeschaltet werden.
quelle