GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Lesen von Zeilen aus einer Datei mit Bash:Für Vs. Während?

Ich versuche, eine Textdatei zu lesen und mit jeder Zeile etwas zu tun, indem ich ein Bash-Skript verwende.

Ich habe also eine Liste, die so aussieht:

server1
server2
server3
server4

Ich dachte, ich könnte dies mit einer While-Schleife wie folgt durchlaufen:

while read server; do
  ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt

Die While-Schleife stoppt nach 1 Durchlauf und führt daher nur uname -a aus auf server1

Mit einer for-Schleife mit cat funktioniert es jedoch gut:

for server in $(cat /home/kenny/list_of_servers.txt) ; do
  ssh $server "uname -a"
done

Noch verblüffender für mich ist, dass dies auch funktioniert:

while read server; do
  echo $server
done < /home/kenny/list_of_servers.txt

Warum stoppt mein erstes Beispiel nach der ersten Iteration?

Akzeptierte Antwort:

Der for Schleife ist hier in Ordnung. Beachten Sie jedoch, dass dies daran liegt, dass die Datei Maschinennamen enthält, die keine Leerzeichen oder Globbing-Zeichen enthalten. for x in $(cat file); do … funktioniert nicht, um die Zeilen von file zu durchlaufen im Allgemeinen, weil die Shell zuerst die Ausgabe des Befehls cat file aufteilt überall dort, wo es Leerzeichen gibt, und behandelt dann jedes Wort als Glob-Muster, also \[?* werden weiter ausgebaut. Sie können for x in $(cat file) erstellen sicher, wenn Sie daran arbeiten:

set -f
IFS='
'
for x in $(cat file); do …

Verwandte Lektüre:Dateien mit Leerzeichen im Namen durchlaufen?; Wie kann ich Zeile für Zeile aus einer Variablen in Bash lesen?; Warum wird while IFS= read so oft verwendet, statt IFS=; while read.. ? Beachten Sie dies bei Verwendung von while read , lautet die sichere Syntax zum Lesen von Zeilen while IFS= read -r line; do … .

Wenden wir uns nun dem zu, was mit Ihrem while read schief geht Versuch. Die Umleitung aus der Serverlistendatei gilt für die gesamte Schleife. Wenn also ssh läuft, kommt seine Standardeingabe aus dieser Datei. Der ssh-Client kann nicht wissen, wann die entfernte Anwendung möglicherweise von ihrer Standardeingabe lesen möchte. Sobald also der ssh-Client eine Eingabe bemerkt, sendet er diese Eingabe an die Remote-Seite. Der ssh-Server dort ist dann bereit, diese Eingabe an den Remote-Befehl weiterzugeben, falls er dies wünscht. In Ihrem Fall liest der Remote-Befehl niemals Eingaben, sodass die Daten verworfen werden, aber die Client-Seite weiß nichts davon. Ihr Versuch mit echo hat funktioniert, weil echo liest niemals irgendwelche Eingaben, es lässt seine Standardeingabe in Ruhe.

Es gibt einige Möglichkeiten, wie Sie dies vermeiden können. Sie können ssh mit -n anweisen, nicht von der Standardeingabe zu lesen Option.

while read server; do
  ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt

Das -n Option teilt tatsächlich ssh mit um seine Eingabe von /dev/null umzuleiten . Sie können dies auf Shell-Ebene tun und es funktioniert für jeden Befehl.

while read server; do
  ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt

Eine verlockende Methode, um zu vermeiden, dass die Eingabe von ssh aus der Datei kommt, besteht darin, die Umleitung auf read zu setzen Befehl:while read server </home/kenny/list_of_servers.txt; do … . Dies wird nicht funktionieren, da es dazu führt, dass die Datei jedes Mal erneut geöffnet wird, wenn der read wird Befehl ausgeführt wird (es würde also die erste Zeile der Datei immer und immer wieder lesen). Die Umleitung muss auf der gesamten While-Schleife erfolgen, damit die Datei einmal für die Dauer der Schleife geöffnet wird.

Verwandte:BashScript funktioniert vom Terminal aus, aber nicht von CronTab?

Die allgemeine Lösung besteht darin, die Eingabe für die Schleife auf einem anderen Dateideskriptor als der Standardeingabe bereitzustellen. Die Shell verfügt über Konstrukte, um Eingaben und Ausgaben von einer Deskriptornummer zu einer anderen weiterzuleiten. Hier öffnen wir die Datei auf Dateideskriptor 3 und leiten den read um Standardeingabe des Befehls aus Dateideskriptor 3. Der ssh-Client ignoriert offene Nicht-Standard-Deskriptoren, also ist alles in Ordnung.

while read server <&3; do
  ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt

In bash wird der read Der Befehl hat eine spezielle Option zum Lesen aus einem anderen Dateideskriptor, sodass Sie read -u3 server schreiben können .

Verwandte Lektüre:Dateideskriptoren und Shell-Scripting; Wann würden Sie einen zusätzlichen Dateideskriptor verwenden?


Linux
  1. Beispiele für Bash-For- und While-Schleifen

  2. Lesen aus einer Datei in Assembly

  3. Hinzufügen eines Zeitstempels zu einem Dateinamen mit mv in BASH

  4. Lesen von Rdata-Dateien mit unterschiedlicher Kodierung

  5. bash + liest Variablen und Werte aus der Datei per Bash-Skript

So lesen Sie Dateien Zeile für Zeile in Bash

So lesen Sie eine Datei Zeile für Zeile in Bash

Bash For Loop mit praktischen Beispielen

Bash-Umleitung mit Beispielen erklärt

Bash-Scripting:So lesen Sie Daten aus Textdateien

Bash-Scripting Teil 2 – For- und While-Schleifen mit Beispielen