Bash ile bir dosyadan satırları okuma: vs.


36

Bir metin dosyasını okumaya ve her satırda bir bash betiği kullanarak bir şeyler yapmaya çalışıyorum.

Öyleyse, şuna benzeyen bir listem var:

server1
server2
server3
server4

Bir süre döngü kullanarak, bunun üzerine döngü olabilir düşündüm:

while read server; do
  ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt

While döngüsü 1 işlemden sonra durur, bu nedenle yalnızca uname -asunucu1'de çalışır

Ancak, cat kullanan bir for döngüsü ile düzgün çalışır:

for server in $(cat /home/kenny/list_of_servers.txt) ; do
  ssh $server "uname -a"
done

Benim için daha da şaşırtıcı olan, bunun da işe yaradığıdır:

while read server; do
  echo $server
done < /home/kenny/list_of_servers.txt

Neden ilk örneğim ilk tekrardan sonra duruyor?

Yanıtlar:


25

forDöngü burada gayet iyi. Ancak bunun, dosyanın boşluk karakterleri veya globbing karakterleri içermeyen makine isimlerini içerdiğinden kaynaklandığını unutmayın. genel olarak for x in $(cat file); do …çizgileri üzerinde yineleme yapmak için çalışmaz file, çünkü kabuk önce çıktıyı komuttan cat fileboşluk olan herhangi bir yere böler ve sonra her kelimeyi bir küre şekli olarak ele alır, böylece \[?*daha da genişler. Üzerinde for x in $(cat file)çalışırsanız güvenli yapabilirsiniz :

set -f
IFS='
'
for x in $(cat file); do 

İlgili okuma: Adlarda boşluk bulunan dosyalar arasında dolaşmak. ; Bir değişkenden bash satır satır satır nasıl okuyabilirim? ; Bunun yerine neden while IFS= readbu kadar sık ​​kullanılıyor IFS=; while read..? Kullanırken while read, satırları okumak için güvenli sözdizimi olduğunu unutmayın while IFS= read -r line; do ….

Şimdi, while readdenemenizde neyin yanlış gittiğine bakalım . Sunucu listesi dosyasından yeniden yönlendirme, tüm döngüye uygulanır. Yani sshçalıştığında, standart girdi o dosyadan gelir. Ssh istemcisi, uzak uygulamanın ne zaman standart girişinden okumak isteyebileceğini bilemez. Böylece ssh istemcisi bazı girişleri fark ettiğinde, o girişi uzak tarafa gönderir. Buradaki ssh sunucusu bu girişi isterse uzak komuta beslemeye hazırdır. Sizin durumunuzda, remote komutu hiçbir zaman herhangi bir girişi okumaz, böylece veriler biter, ancak müşteri tarafı bunun hakkında hiçbir şey bilmez. echoÇalıştığınız girişim echohiç bir girişi okumadığı için standart girişi tek başına bırakır.

Bundan kaçınmanın birkaç yolu vardır. Ssh -nseçeneğine standart girdiden okumamasını söyleyebilirsiniz .

while read server; do
  ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt

-nAslında seçenek söyler sshonun girişini yönlendirmek için /dev/null. Bunu kabuk düzeyinde yapabilirsiniz ve herhangi bir komut için işe yarar.

while read server; do
  ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt

Önlemek ssh'nin giriş dosyasından çıkan A cazip yöntem üzerinde yeniden yönlendirme koymaktır readkomutu: while read server </home/kenny/list_of_servers.txt; do …. Bu çalışmaz, çünkü readkomut her çalıştırıldığında dosyanın tekrar açılmasına neden olur (böylece dosyanın ilk satırını tekrar tekrar okuyacaktır). Yeniden yönlendirmenin tüm döngü boyunca olması gerekir, böylece dosya döngü süresince bir kez açılır.

Genel çözüm, standart girdi dışındaki bir dosya tanımlayıcısındaki döngüye girdi sağlamaktır . Kabuk, giriş ve çıkışı bir tanımlayıcı numaradan diğerine feribot yapacak şekilde inşa edilmiştir. Burada dosyayı dosya tanımlayıcı 3'te açıyoruz ve readkomutun standart girişini dosya tanımlayıcı 3'ten yeniden yönlendiriyoruz . Ssh istemcisi standart olmayan açıklayıcıları görmezden geliyor.

while read server <&3; do
  ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt

Bash'ta, readkomutun farklı bir dosya tanımlayıcısından okumak için belirli bir seçeneği var, böylece yazabilirsiniz read -u3 server.

İlgili okuma: Dosya tanımlayıcıları ve kabuk komut dosyası ; Ne zaman ek bir dosya tanımlayıcı kullanırsınız?


5
Dostum, bu yığın değişimi kesinlikle sunucu arızalarından farklı. Buradaki insanlar gerçekten sadece cevap vermekle kalmıyor, aynı zamanda eğitiyorlar. Çok teşekkürler.
Kenny Rasschaert 10:11

26

Sen kullanmalısınız whiledeğilfor . Böyle bir döngüde standart girdiyi yutan komutlardan kaçınmanın yolu basitçe başka bir dosya tanımlayıcı kullanmaktır :

while read -u 9 server; do
  ssh $server "uname -a"
done 9< /home/kenny/list_of_servers.txt

Daha fazla bilgi için, help [r]ead( gerçekten ) ve nedenini açıklayan başka bir makale .


1
İlginç, daha önce hiç dosya tanımlayıcı kullanmamıştım. -uBayrak ne yapar ? Hangi adam sayfasında bakmam gerektiğinden emin değilim.
Kenny Rasschaert

Read komutu, kabuk bash'ın bir parçasıdır, bu nedenle bash basamağında, okuma paragrafındaki "SHELL BUILTIN COMMANDS" bölümündeki man sayfasındadır. "-U fd fd dosya tanımlayıcısından girişi oku." bu paragrafta
f4m8

6
Alternatif olarak help read- kabuk yerleşikleri için sayfaların helpeşdeğerini alma komutu man.
10-0

22

İlk kodunuzda sshSTDIN’den “çalınacak” while. Bunu önlemek için -nseçenek ekleyin ssh. Kimden man ssh:

-n     Redirects stdin from /dev/null (actually, prevents reading from stdin).

Tamam, neden for döngüsü ile olmadığına dair bir fikriniz var mı?
Kenny Rasschaert

7
Çünkü fordöngü, verileri girdi olarak değil parametre olarak alır.
Manatwork

1
Siz bayım, bir centilmen ve bilginsiniz.
Kenny Rasschaert

0

Varsayılan standart girdi işleme ssh, kalan satırı while döngüsünden boşaltır.

Bu sorunu önlemek için, sorunlu komutun standart girdiyi nereden okuduğunu değiştirin. Komuta standart bir giriş yapılması gerekmiyorsa, özel /dev/nullcihazdan standart girişi okuyun .

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.