2008-12-06 13 views
25

Od pewnego czasu walę głową o ścianę.Bash podczas przerwania pętli odczytu na początku

Chcę SSH w zestaw maszyn i sprawdzić, czy są one dostępne (przyjmowanie połączeń i nie są używane). Stworzyłem mały skrypt, tssh, który właśnie to:

#!/bin/bash 

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

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

Ten skrypt działa poprawnie. Powracanie 255, jeśli wystąpił problem z połączeniem, 1 jeśli maszyna jest zajęta i 0, jeśli wszystko jest w porządku. Jeśli ktoś zna lepszy sposób, aby to zrobić, proszę dać mi znać.

W następnej kolejności próbuję wywołać tssh na moim zestawie maszyn za pomocą pętli read while, i tu wszystko idzie źle. Pętla kończy działanie, gdy tssh zwróci 0 i nigdy nie zakończy pełnego zbioru.

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

Początkowo myślałem, że to problem z podpowłoką, ale najwyraźniej nie. Każda pomoc, wraz z komentarzem na temat stylu/treści, byłaby bardzo doceniana! wiem Idę kopać siebie, kiedy dowiem się dlaczego ...

+0

Mam podobny problem z Perl Net :: SSH :: Perl @ Foo-Bah opisał ten problem bardzo dobrze, a rozwiązaniem jest dodanie pusty ciąg jako parametr do cmd: https://metacpan.org/pod/Net::SSH::Perl#out-err-exit-ssh-cmd-cmd-stdin my ($ stdout, $ stderr, $ exit) = $ ssh-> cmd ($ cmd, ""); – Thomas

Odpowiedz

4

Nie wiem, czy to pomoże, ale czystsze sposób pisania, który byłby

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

Ponadto, jeśli masz GNU Coreutils, możesz użyć 'seq 0 20' zamiast komendy ruby. –

+0

@Paul Dzięki, to działa! Teraz, gdybym tylko wiedział dlaczego! Może to przecież problem z podpowłoką. Na pewno przypomina o wcześniejszych problemach z podsłuchem, jakie miałem. @Jouni Wymieniłem część brzydkiego rubinu na seq. Nie wiedziałem o tym rozkazie, dziękuję. @ Wszystkie Dowolny wgląd w to, dlaczego ta poprawka działa? –

+1

@Paul To jest problem ssh. Zrobię post –

3

jestem również pewności, dlaczego to się nie powiedzie, ale lubię xargs i seq:

seq 0 20 | xargs -n1 tssh MYBOXES 
2

nie mogę uwierzyć, że to był wynik z 0 który złamał pętli, można przetestować przeciwko temu, zastępując swoją komendę tssh w pętli z "/bin/true ", który zwraca także 0.

jeśli chodzi o styl, nie rozumiem, dlaczego prosty skrypt z zapętloną powłoką potrzebuje ruby, perla, seqa lub joty, lub jakiegokolwiek innego pliku binarnego, którego nie ma na moim * BSD.

alternatywnie można użyć do konstrukcji muszli wbudowanych pętli, która działa przynajmniej w ksh, bash:

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

wpadłem na to dzisiaj - rsh i/lub ssh może złamać podczas odczytu pętli należytej do tego przy użyciu stdin. Wpisałem -n do linii ssh, która powstrzymuje ją przed próbą użycia stdin i naprawiła problem.

3

Jak wspomniał Kaii, naprawdę trudno przesłać nazwę ruby ​​lub seq (która nie zadziała na komputerach BSD lub OSX) tylko po to, aby wyprowadzić zakres liczb. Jeśli jesteś zadowolony z używania bash, możesz:

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

Wierzę, że to powinno działać dla bash 2.05b i wyżej.

36

Chris ma rację. Źródłem przerwania pętli był SSH używający stdin, jednak pistolety są poprawne w użyciu metod pętli.

Jeśli przechodzisz przez wejście (na przykład plik z listą nazw hostów) i wywołujesz SSH, musisz przekazać parametr -n, w przeciwnym razie twoja pętla oparta na danych wejściowych nie powiedzie się.

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

mówić o problemie "rabunku Piotra, by zapłacić Pawłowi". Od wielu godzin walczyłem, aby dowiedzieć się, dlaczego mój ssh zabijał mnie podczas czytania.

Innym sposobem na trzymanie się pętli odczytu i trzymanie swojego ssh w tym samym czasie jest użycie przełącznika "-n", aby STDIN na ssh/dev/null. Działa jak czar dla mnie:

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

(. Staram się zawsze używać „-x” zbyt uniknąć marnowania czasu negocjacji X w tunelu)

31

w konstrukcie

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

standardowe wejście widziane przez pętlę while jest wyjściem z something. Domyślnym zachowaniem jest odczyt standardowego wejścia. W ten sposób można robić rzeczy, jak

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

Teraz, z tym mówi się, gdy pierwsza wartość jest odczytywana, pierwsze polecenie ssh odczyta całą wejście od something. Następnie, do czasu zakończenia ssh, nie pozostaje żadne wyjście i zatrzymuje się read.

Poprawka to ssh -n ... np.

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

Najlepsza odpowiedź! – Jer

2

Większość odpowiedzi dotyczy ssh. Inne polecenia przechwytują również standardowe wejście i nie mają opcji -n. Powinno to dotyczyć wszelkich innych poleceń. To powinno również działać dla ssh.

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