Snažím se číst textový soubor a dělat něco s každým řádkem pomocí bash skriptu.
Takže mám seznam, který vypadá takto:
server1
server2
server3
server4
Myslel jsem, že bych to mohl opakovat pomocí smyčky while, například takto:
while read server; do
ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt
Cyklus while se zastaví po 1 spuštění, takže spustí pouze uname -a
na serveru1
Nicméně se smyčkou for pomocí cat to funguje dobře:
for server in $(cat /home/kenny/list_of_servers.txt) ; do
ssh $server "uname -a"
done
Ještě více mě zaráží, že to také funguje:
while read server; do
echo $server
done < /home/kenny/list_of_servers.txt
Proč se můj první příklad zastaví po první iteraci?
Přijatá odpověď:
for
smyčka je zde v pořádku. Ale všimněte si, že je to proto, že soubor obsahuje názvy počítačů, které neobsahují žádné mezery ani globbingové znaky. for x in $(cat file); do …
nefunguje při iteraci přes řádky file
obecně, protože shell nejprve rozdělí výstup z příkazu cat file
kdekoli je mezera, a poté každé slovo považuje za vzor globus, takže \[?*
jsou dále rozšiřovány. Můžete vytvořit for x in $(cat file)
bezpečné, pokud na něm pracujete:
set -f
IFS='
'
for x in $(cat file); do …
Související čtení:Procházení souborů s mezerami v názvech?; Jak mohu číst řádek po řádku z proměnné v bash?; Proč je while IFS= read
používá se tak často místo IFS=; while read..
? Pamatujte, že při použití while read
, bezpečná syntaxe pro čtení řádků je while IFS= read -r line; do …
.
Nyní se podívejme na to, co se s vaším while read
pokazilo pokus. Přesměrování ze souboru seznamu serverů platí pro celou smyčku. Takže když ssh
běží, jeho standardní vstup pochází z tohoto souboru. Ssh klient nemůže vědět, kdy může vzdálená aplikace chtít číst ze svého standardního vstupu. Takže jakmile ssh klient zaznamená nějaký vstup, odešle tento vstup na vzdálenou stranu. Ssh server je pak připraven poskytnout tento vstup vzdálenému příkazu, pokud by to chtěl. Ve vašem případě vzdálený příkaz nikdy nečte žádný vstup, takže data skončí zahozená, ale klientská strana o tom nic neví. Váš pokus s echo
fungovalo, protože echo
nikdy nečte žádný vstup, nechává svůj standardní vstup na pokoji.
Existuje několik způsobů, jak se tomu vyhnout. Pomocí -n
můžete ssh říct, aby nečetl ze standardního vstupu možnost.
while read server; do
ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt
-n
volba ve skutečnosti říká ssh
přesměrovat svůj vstup z /dev/null
. Můžete to udělat na úrovni shellu a bude to fungovat pro jakýkoli příkaz.
while read server; do
ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt
Lákavou metodou, jak se vyhnout vstupu ssh ze souboru, je umístit přesměrování na read
příkaz:while read server </home/kenny/list_of_servers.txt; do …
. To nebude fungovat, protože to způsobí, že se soubor znovu otevře při každém read
se provede příkaz (takže by četl první řádek souboru znovu a znovu). Přesměrování musí být v celé smyčce while, aby byl soubor otevřen jednou po dobu trvání smyčky.
Obecným řešením je poskytnout vstup do smyčky na jiném deskriptoru souboru, než je standardní vstup. Shell má konstrukce pro přenos vstupu a výstupu z jednoho čísla deskriptoru na druhé. Zde otevřeme soubor na deskriptoru souboru 3 a přesměrujeme read
standardní vstup příkazu z deskriptoru souboru 3. Klient ssh ignoruje otevřené nestandardní deskriptory, takže je vše v pořádku.
while read server <&3; do
ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt
V bash, read
příkaz má specifickou možnost číst z jiného deskriptoru souboru, takže můžete napsat read -u3 server
.
Související čtení:Deskriptory souborů a skriptování shellu; Kdy byste použili další deskriptor souboru?