Döngü kullanarak birden çok sunucuya ssh kullanmak


75

Bir dosya var servers.txtsunucuların listesi ile,:

server1.mydomain.com
server2.mydomain.com
server3.mydomain.com

Dosyayı satır satır okudum whileve her satırda yankı yazdığımda , tüm beklendiği gibi çalışıyor. Tüm satırlar yazdırılıyor.

$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com

Ancak, tüm sunuculara ssh yapmak ve bir komutu çalıştırmak istediğimde aniden whiledöngüm çalışmamaya başladı:

$ 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

Bu, yalnızca listedeki ilk sunucuya bağlanır, hepsine değil. Burada ne olduğunu anlamıyorum. Biri lütfen açıklayabilir mi?

Bu, daha da garip, çünkü fordöngü kullanımı iyi çalışıyor:

$ 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

Belirli bir şey olmalı ssh, çünkü diğer komutlar iyi çalışıyor, örneğin ping:

$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt

4
kullanınansible
Matt

Yanıtlar:


98

ssh standart girişinizin kalanını okuyor.

while read HOST ; do … ; done < servers.txt

readstdin'den okur. <Yönlendirmeleri bir dosyadan stdin'i.

Ne yazık ki, çalıştırmaya çalıştığınız komut da stdin okur, bu nedenle dosyanızın geri kalanını yeme rüzgarla sarılır. Şunu açıkça görebilirsiniz:

$ while read HOST ; do echo start $HOST end; cat; done < servers.txt 
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com

catKalan iki çizginin ne kadar yediğini (ve yankılandıklarını) görün (Beklendiği gibi okumuş olsaydı, her satırın ana bilgisayarı üzerinde "başlangıç" ve "son" olurdu.)

Neden forçalışıyor

Kişisel forçizgi stdin'e yönlendirmez. (Aslında, servers.txtilk yinelemeden önce dosyanın tüm içeriğini belleğe okur ). Bu yüzden sshstdin'i terminalden okumaya devam eder (ya da komut dosyanızın nasıl adlandırıldığına bağlı olarak, muhtemelen hiçbir şey).

Çözüm

En azından bash'ta readfarklı bir dosya tanımlayıcı kullanabilirsiniz.

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

çalışması gerekir. 10sadece seçtiğim keyfi bir dosya numarası. 0, 1 ve 2 tanımlanmış anlamlara sahiptir ve tipik olarak açılış dosyaları mevcut ilk numaradan başlayacaktır (yani 3 kullanılacaktır). 10 böylece yoldan uzak durmaya yetecek kadar yüksek, ancak bazı mermilerde limitin altında olacak kadar düşük. Ayrıca onun güzel bir tur numarası ...

Alternatif Çözüm 1: -n

As McNisse içinde işaret onun / onu cevap , OpenSSH istemcisi bir sahiptir -nStdin okumasını engellemek edeceğiz seçeneği. Bu, özel bir durumda iyi sonuç verir ssh, ancak elbette başka komutlar da eksik olabilir - diğer çözümler, hangi komutun stdininizi yediğinden bağımsız olarak çalışır.

Alternatif Çözüm 2: ikinci yönlendirme

Görünüşe göre (denedim, denedim, en azından Bash sürümümde çalışıyor ...) ikinci bir yönlendirme yap, şuna benzer:

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

Bunu herhangi bir komutla kullanabilirsiniz, ancak terminal girişinin komuta gitmesini gerçekten istiyorsanız, zor olacaktır.


1
numara 10nereden geliyor?
Martin Vegter

2
@ MartinVegter Yaptım. 0/1/2 stdin, stdout ve stderr. İstediğiniz herhangi bir numarayı seçebilirsiniz - bash, muhtemelen işletim sistemi sınırına kadar oldukça yükseğe çıkmanıza izin verir. Diğer mermiler sizi daha az kısıtlayabilir ...
derobert

@MartinVegter Yorumlarınızın her ikisine de cevap vermek için düzenleme yaptım.
derobert

Başka bir alternatif: exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&- Bu, dosya tanımlayıcısını 3 orijinal stdin'in bir yedeği haline getirir, ardından ssh'nin stdin'i için kullanır, sonra bittiğinde yedek FD'yi kapatır. Bu, POSIX uyumlu herhangi bir kabukta çalışır.
Richard Hansen,

8
Bu -useçenek POSIX tarafından desteklenmemektedir ve bu nedenle #!/bin/shkomut dosyaları için kullanılmamalıdır ; kullanmak read HOST <&10yerine. Ayrıca, POSIX yalnızca 0'dan 9'a kadar olan dosya tanımlayıcılarını desteklemek için kabuklar gerektirir, bu nedenle 10<servers.txtkomut dosyası tam olarak uyuyorsa kullanılamaz.
Richard Hansen,

28

Derobert tarif ettiği gibi senin sshokur stdin.

Bu davranışı değiştirmek için stdin okumasını engellemek için -n ssh komutunu ekleyebilirsiniz.

ssh -n $HOST "uname -a"

10

Muhtemelen aslında kullanarak daha iyi olurdu Pssh gelen paralel SSH projesi.

pssh -h $hostfile -t $timeout -i $commands

-ietkileşimli demektir. pssh ayrıca paralel bir scp ve paralel rsync ile birlikte gelir. Güzel olan şey, zaman uyumsuz olarak çalışması ve istediğiniz kadar iş parçacığı çalıştırması. Varsayılan (-i / interaktif olmayan), $ outputdir / $ hostname ile yaptığı stdout / stderr için ayrı dizinlere çıktı sağlamaktır.


3

Kendinizi bu tür bir işi oldukça sık bulursanız, Kumaş denemelisiniz.

Komutları izleyerek , sadece ihtiyacınız olan çoğu durumda Kumaş'ı kurunsudo apt-get install fabric

fabfile.pyAşağıdaki kodla adlandırılmış bir dosya oluşturun :

from fabric.api import env, run

env.hosts = ['server1.mydomain.com',
             'server1.mydomain.com',
             'server1.mydomain.com']

def mytask():
    run('uname -a')

Sonra koşmak fab mytaskistediğiniz sonucu verecektir.


1

Böyle bir komutu kullanmak daha kolaydır:

for f in `cat servers.txt`; do ssh $f uname -a; done

Genelde böyle yaparım:

for f in `cat servers.txt`; do echo "### $f ###"; ssh $f uname -a; done

echoKalmış, ya da bağlanamaz hangi sunucu görmektir.


1

Çünkü bir ssh komutu ve tüm akışı standart girdiden alır, while cümlesi tarafından beslenir.

Ssh dizgesini başka bir kaynağa geçirmek için bir boru kullanabilirsiniz:

echo "" | ssh ...

örnek :

while read HOST ; do echo "" | ssh $HOST "uname -a" ; done < servers.txt

Bir süre içinde tüm ssh komutlarının stdin girişi başka bir kaynağa geçirilmelidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.