2008-12-06 9 views
25

Ich habe mit diesem einen Kopf für eine Weile gegen die Wand geschlagen.Bash, während Leseschleife brechen früh

Ich möchte SSH in eine Reihe von Maschinen und überprüfen, ob sie verfügbar sind (Annahme von Verbindungen und nicht verwendet werden). Ich habe ein kleines Skript erstellt, TSSH, das genau das tut:

#!/bin/bash 

host=$1 
timeout=${2:-1} 

ssh -qo "ConnectTimeout $timeout" $host "[ \`who | cut -f1 | wc -l \` -eq 0 ] && exit 0 || exit 1" 

Dieses Skript funktioniert ordnungsgemäß. Rückgabe 255 wenn ein Verbindungsproblem aufgetreten ist, 1 wenn die Maschine beschäftigt ist und 0 wenn alles gut ist. Wenn jemand einen besseren Weg kennt, lass es mich wissen.

Also nächste versuche ich und tssh auf meinem Satz von Maschinen mit einer Weile lesen Schleife, und das ist, wo alles schief geht. Die Schleife wird beendet, sobald tssh 0 zurückgibt und niemals die vollständige Menge abschließt.

while read nu ; do tssh "MYBOXES$nu" ; done < <(ruby -e '(0..20).each { |i| puts i }') 

Zuerst dachte ich, das war ein Subshell-Problem, aber anscheinend nicht. Jede Hilfe, zusammen mit Kommentaren zu Stil/Inhalt, wäre sehr willkommen! Ich weiß, ich werde mich treten, wenn ich, warum erfahren ...

+0

Ich bin ein ähnliches Problem mit Perl-Net mit :: SSH :: Perl, @ Foo-Bah das Problem wirklich gut beschrieben hat, und die Lösung ist eine leere Zeichenfolge als Parameter an die cmd hinzuzufügen: https://metacpan.org/pod/Net::SSH::Perl#out-err-exit-ssh-cmd-cmd-stdin mein ($ stdout, $ stderr, $ exit) = $ ssh-> cmd ($ cmd, ""); – Thomas

Antwort

4

Weiß nicht, ob es helfen würde, aber eine saubere Art und Weise des Schreibens, die

for nu in `ruby -e '(0..20).each { |i| puts i}'`; do 
    tssh "MYBOXES$nu" 
done 
+3

Wenn Sie GNU Coreutils haben, können Sie anstelle des Ruby-Befehls auch 'seq 0 20' verwenden. –

+0

@Paul Danke, das funktioniert! Wenn ich nur wüsste warum! Vielleicht ist es schließlich ein Subshell-Problem. Sicherlich erinnert es an frühere Subshell-Probleme, die ich hatte. @Jouni Ich ersetzte den hässlichen Rubin Teil mit Seq. War mir dieses Kommando nicht bewusst, danke. @All Jeder Einblick, warum dieser Fix funktioniert? –

+1

@Paul Es ist ein SSH-Problem. Ich werde einen Beitrag machen –

3

würde ich bin auch nicht sicher, warum es nicht, aber ich mag xargs und seq:

seq 0 20 | xargs -n1 tssh MYBOXES 
2

ich kann nicht glauben, dass es das Ergebnis von 0 war, dass Ihre Schleife brach, können Sie gegen diese testen, indem Sie Ihren tssh Befehl in der Schleife mit "ersetzen/bin/true ", die ebenfalls 0 zurückgibt.

in Bezug auf Stil verstehe ich nicht, warum ein einfaches Looping-Shell-Skript Ruby, Perl, Seq oder Jot oder eine andere Binärdatei, die nicht auf meinem * BSD ist.

können Sie alternativ die Schalen für Schleifenkonstrukt builtin verwendet werden, die zumindest in KSH arbeitet, bash:

for ((i=0; $i<=20; i++)); do 
    tssh "MYBOXES$i" 
done 
6

Ich lief in diesem heute - rsh und/oder ssh brechen kann eine Weile durch Leseschleife dazu mit stdin. Ich habe ein -n in die ssh-Zeile gesetzt, was es daran hindert, stdin zu verwenden, und es hat das Problem behoben.

3

Wie Kawaii erwähnt hat, ist es wirklich übertrieben, ruby ​​oder seq aufzurufen (was auf BSD- oder OSX-Rechnern nicht funktioniert), nur um eine Reihe von Zahlen auszugeben. Wenn Sie mit der Verwendung von Bash glücklich sind, können Sie:

for i in {0..20}; do 
    # command 
done 

Ich glaube, das sollte für bash 2.05b und höher funktionieren.

36

Chris hat Recht. Die Quelle des Schleifenbruchs war SSH unter Verwendung von Stdin, jedoch ist die Verwendung einer Schleifenmethodik korrekt.

Wenn Sie eine Eingabe durchlaufen (eine Datei mit einer Liste von Hostnamen zum Beispiel) und SSH aufrufen, müssen Sie den Parameter -n übergeben, da andernfalls Ihre auf Eingaben basierende Schleife fehlschlägt.

while read host; do 
    ssh -n $host "remote command" >> output.txt 
done << host_list_file.txt 
2

reden über ein "berauben Peter Peter Paul Problem zu bezahlen. Ich hatte stundenlang gekämpft, um herauszufinden, warum mein ssh mein Ding tötete | während ich eine Schleife las.

Eine andere Möglichkeit, mit einer while-Read-Schleife zu bleiben und Ihre ssh gleichzeitig zu behalten, ist die Verwendung der "-n" -Schaltfläche, um STDIN auf ssh/dev/null zu setzen. Funktioniert wie ein Zauber für mich:

#!/bin/bash 
[...] 
something|while read host 
    do 
     ssh -nx ${host} fiddleAround 
    done 

(. Ich neige dazu, immer die "-x" zu nutzen und verschwendete Zeit verhandelt X in einem Tunnel zu vermeiden)

31

In dem Konstrukt

something | 
while read x; do 
    ssh ... 
done 

Die Standardeingabe aus der While-Schleife ist die Ausgabe von something.

Das Standardverhalten von ssh ist das Lesen der Standardeingabe. Auf diese Weise können Sie Dinge wie

cat id_rsa.pub | ssh new_box "cat - >> ~/.ssh/authorized_keys" 

jetzt zu tun, mit, dass gesagt wird, wenn der erste Wert gelesen wird, wird der erste Befehl ssh die gesamte Eingabe von something lesen. Bis zum Ende von ssh ist keine Ausgabe mehr vorhanden und read stoppt.

Der Fix ist ssh -n ... z.B.

cat /etc/hosts | awk '{print $2}' | while read x; do 
    ssh -n $x "do_something_on_the_machine" 
done 
+0

Die beste Antwort! – Jer

2

Die meisten Antworten beziehen sich nur auf ssh. Andere Befehle übernehmen ebenfalls die Standardeingabe und haben keine Option -n. Dies sollte alle anderen Befehle adressieren. Dies sollte auch für ssh funktionieren.

while read x; do 
    # Make sure command does not hijack stdin 
    echo "" | command $x 
done < /path/to/some/file