Mam plik servers.txt
z listą serwerów:
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
kiedy czytam plik linia po linii while
i echo każdej linii, wszystko działa zgodnie z oczekiwaniami. Wszystkie linie są drukowane.
$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
Jednak gdy chcę ssh do wszystkich serwerów i wykonać polecenie, nagle moja while
pętla przestaje działać:
$ 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
To łączy się tylko z pierwszym serwerem na liście, a nie z wszystkimi. Nie rozumiem, co się tutaj dzieje. Czy ktoś może wyjaśnić?
Jest to nawet dziwniejsze, ponieważ użycie for
pętli działa dobrze:
$ 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
Musi to być coś konkretnego ssh
, ponieważ inne polecenia działają dobrze, takie jak ping
:
$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt
ssh
scripting
io-redirection
Martin Vegter
źródło
źródło
ansible
Odpowiedzi:
ssh
czyta resztę twojego standardowego wejścia.read
czyta ze standardowego. Do<
przekierowania stdin z pliku.Niestety polecenie, które próbujesz uruchomić, również odczytuje stdin, więc zjada resztę pliku. Możesz to wyraźnie zobaczyć za pomocą:
Zauważ, jak
cat
zjadłem (i echo) pozostałe dwie linie. (Gdyby przeczytać to zgodnie z oczekiwaniami, każda linia miałaby „początek” i „koniec” wokół hosta).Dlaczego
for
działaTwoja
for
linia nie przekierowuje do standardowego wejścia. (W rzeczywistości odczytuje całą zawartośćservers.txt
pliku do pamięci przed pierwszą iteracją). Więcssh
nadal odczytuje stdin z terminala (lub ewentualnie nic, w zależności od tego, jak wywoływany jest twój skrypt).Rozwiązanie
Przynajmniej w bash możesz
read
użyć innego deskryptora pliku.powinien działać.
10
to tylko wybrany przeze mnie numer pliku. 0, 1 i 2 mają zdefiniowane znaczenie, a zazwyczaj otwieranie plików rozpocznie się od pierwszego dostępnego numeru (więc 3 będzie dalej używane). 10 jest więc wystarczająco wysoka, aby trzymać się z daleka, ale wystarczająco niska, aby być poniżej limitu w niektórych pociskach. Plus jest to ładny okrągły numer ...Alternatywne rozwiązanie 1: -n
Jak wskazuje McNisse w swojej odpowiedzi , klient OpenSSH ma
-n
opcję, która uniemożliwi odczytanie standardowego wejścia. Działa to dobrze w konkretnym przypadkussh
, ale oczywiście innym poleceniom może tego brakować - inne rozwiązania działają niezależnie od tego, które polecenie zjada twoje standardowe wejście.Alternatywne rozwiązanie 2: drugie przekierowanie
Możesz najwyraźniej (jak w, próbowałem, działa przynajmniej w mojej wersji Basha ...) zrobić drugie przekierowanie, które wygląda mniej więcej tak:
Możesz użyć tego z dowolną komendą, ale będzie to trudne, jeśli naprawdę chcesz, aby wejście terminala trafiło do komendy.
źródło
10
pochodzi ten numer ?exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&-
powoduje, że deskryptor pliku 3 tworzy kopię zapasową oryginalnego stdin, następnie używa go do stdin ssh, a po zakończeniu zamyka FD kopii zapasowej. Działa to na dowolnej powłoce kompatybilnej z POSIX.-u
Opcja nie jest obsługiwana przez POSIX, a zatem nie powinny być wykorzystywane do#!/bin/sh
skryptów; użyjread HOST <&10
zamiast tego. Ponadto POSIX wymaga tylko powłok do obsługi deskryptorów plików od 0 do 9, więc10<servers.txt
nie można go użyć, jeśli skrypt ma być ściśle zgodny.Jak opisuje derobert
ssh
czyta twojestdin
.Aby zmienić to zachowanie, możesz dodać -n no ssh, aby uniemożliwić odczytanie standardowego wejścia.
źródło
Prawdopodobnie lepiej byłoby użyć pssh z projektu równoległego ssh .
-i
oznacza interaktywny. pssh jest również wyposażony w równoległy scp i równoległy rsync. Zaletą jest to, że działa asynchronicznie i uruchamia tyle wątków, ile chcesz. Domyślnie (non -i / interactive) jest wysyłany do osobnych katalogów dla stdout / stderr, co robi przez $ outputdir / $ hostname.źródło
Jeśli często wykonujesz tego rodzaju zadania, powinieneś wypróbować Fabric
Zainstaluj sieć zgodnie z instrukcjami , w większości przypadków, których potrzebujesz
sudo apt-get install fabric
Utwórz plik o nazwie
fabfile.py
z następującym kodem:Następnie bieg
fab mytask
da wynik, który chcesz.źródło
Łatwiej jest użyć takiego polecenia:
Zwykle lubię to:
Chodzi o
echo
to, który serwer utknął lub nie może się z nim połączyć.źródło
Ponieważ komenda ssh pobiera cały strumień ze standardowego wejścia, zasilanego przez instrukcję while,
Możesz użyć potoku, aby przełączyć standardowe wejście ssh na inne źródło:
przykład:
wejście stdin wszystkich poleceń ssh w pętli while musi zostać przełączone na inne źródło.
źródło