Shell-Skript, während die Zeilenschleife gelesen wird, stoppt nach der ersten Zeile

106

Ich habe das folgende Shell-Skript. Der Zweck besteht darin, jede Zeile der Zieldatei (deren Pfad der Eingabeparameter für das Skript ist) zu durchlaufen und für jede Zeile zu arbeiten. Jetzt scheint es nur mit der allerersten Zeile in der Zieldatei zu funktionieren und stoppt, nachdem diese Zeile verarbeitet wurde. Stimmt etwas mit meinem Skript nicht?

#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets 

FILENAME=$1
count=0

echo "proceed with $FILENAME"

while read LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh $LINE
done < $FILENAME

echo "\ntotal $count targets"

In do_work.shführe ich ein paar sshBefehle aus.

Bischof
quelle
1
Ihr Skript ist in Ordnung, aber möglicherweise stimmt etwas nicht mit do_work.sh
sleepsort
2
Ja, es könnte alle Eingaben auffressen, oder es könnte aufgerufen werden als sourceund einfach beenden oder exec. Aber dieser Code sieht nicht echt aus, das OP würde bemerken, dass das Echo den -eZeilenvorschub richtig anzeigen muss ...
Michael Krelin - Hacker
3
Hat do_work.shlaufen sshdurch Zufall?
Dogbane
1
Ja, do_work.sh führt einige ssh-Befehle aus. etwas besonderes daran?
Bischof
1
Besser Sie zeigen die do_work.shQuelle und laufen auch do.shmit set -xzum Debuggen.
Koola

Antworten:

178

Das Problem ist , dass die do_work.shLäufe sshBefehle und standardmäßig sshliest aus stdin , die ist Ihre Eingabedatei. Infolgedessen wird nur die erste Zeile verarbeitet, da sshder Rest der Datei verbraucht wird und Ihre while-Schleife beendet wird.

Um dies zu verhindern, übergeben Sie die -nOption an Ihren sshBefehl, damit dieser /dev/nullanstelle von stdin gelesen wird .

Dogbane
quelle
1
Sehr nützlich, hat mir geholfen, diesen zsh-Oneliner auszuführen: cat hosts | während Host gelesen; do ssh $ host do_something; fertig
Ratte
3
@rat Du willst immer noch das Nutzlosecat vermeiden . Sie würden denken, dass insbesondere ein Nagetier diesbezüglich vorsichtig sein würde.
Tripleee
while read host ; do $host do_something ; done < /etc/hostswürde es vermeiden. Das ist ein ziemlicher Lebensretter, danke!
Ratte
httpieist ein weiterer Befehl, der standardmäßig STDIN liest und unter demselben Verhalten leidet, wenn er in einer Bash- oder Fish-Schleife aufgerufen wird. Verwenden http --ignore-stdinoder setzen Sie die Standardeingabe /dev/nullwie oben beschrieben.
Raman
12

Im Allgemeinen besteht eine Problemumgehung, die nicht spezifisch sshist, darin, die Standardeingabe für jeden Befehl umzuleiten, der andernfalls die whileEingabe der Schleife verbrauchen könnte .

while read -r LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh "$LINE" </dev/null
done < "$FILENAME"

Das Hinzufügen von </dev/nullist hier der entscheidende Punkt (obwohl das korrigierte Anführungszeichen auch etwas wichtig ist; siehe auch Wann werden Anführungszeichen um eine Shell-Variable gewickelt? ). Sie sollten es verwenden, es read -rsei denn, Sie benötigen speziell das etwas merkwürdige Verhalten, ohne das Sie auskommen -r.

Eine andere Art von Problemumgehung, die etwas spezifisch ist, sshbesteht darin, sicherzustellen, dass sshdie Standardeingabe eines Befehls verknüpft ist, z. B. durch Ändern

ssh otherhost some commands here

Lesen Sie stattdessen die Befehle aus einem Dokument, das bequem (für dieses spezielle Szenario) die Standardeingabe sshfür die Befehle verknüpft:

ssh otherhost <<'____HERE'
    some commands here
____HERE
Tripleee
quelle
5

Die Option ssh -n verhindert, dass der Exit-Status von ssh überprüft wird, wenn HEREdoc verwendet wird, während die Ausgabe an ein anderes Programm weitergeleitet wird. Daher wird die Verwendung von / dev / null als Standard bevorzugt.

#!/bin/bash
while read ONELINE ; do
   ssh ubuntu@host_xyz </dev/null <<EOF 2>&1 | filter_pgm 
   echo "Hi, $ONELINE. You come here often?"
   process_response_pgm 
EOF
   if [ ${PIPESTATUS[0]} -ne 0 ] ; then
      echo "aborting loop"
      exit ${PIPESTATUS[0]}
   fi
done << input_list.txt
jacobm654321
quelle
Das macht keinen Sinn. Das <<EOFüberschreibt die </dev/nullUmleitung. Die <<Umleitung nach dem doneist falsch.
Tripleee
1

Dies geschah mir, weil ich hatte set -eund eine grepin einer Schleife ohne Ausgabe zurückkehrte (was einen Fehlercode ungleich Null ergibt).

JonnyRaa
quelle