O for
loop está bom aqui. Mas observe que isso ocorre porque o arquivo contém nomes de máquinas, que não contêm caracteres de espaço em branco ou globbing. for x in $(cat file); do …
não funciona para iterar sobre as linhas de file
em geral, porque o shell primeiro divide a saída do comando em cat file
qualquer lugar em que haja espaço em branco e, em seguida, trata cada palavra como um padrão glob, para que \[?*
sejam expandidas ainda mais. Você pode se for x in $(cat file)
proteger se trabalhar nele:
set -f
IFS='
'
for x in $(cat file); do …
Leitura relacionada: Fazendo loop em arquivos com espaços nos nomes? ; Como posso ler linha por linha de uma variável no bash? ; Por que é while IFS= read
usado com tanta frequência, em vez de IFS=; while read..
? Observe que, ao usar while read
, a sintaxe segura para ler linhas é while IFS= read -r line; do …
.
Agora vamos ao que deu errado com sua while read
tentativa. O redirecionamento do arquivo da lista de servidores se aplica a todo o loop. Portanto, quando ssh
executado, sua entrada padrão vem desse arquivo. O cliente ssh não pode saber quando o aplicativo remoto pode querer ler de sua entrada padrão. Portanto, assim que o cliente ssh percebe alguma entrada, envia essa entrada para o lado remoto. O servidor ssh então está pronto para alimentar essa entrada para o comando remoto, caso deseje. No seu caso, o comando remoto nunca lê nenhuma entrada; portanto, os dados acabam sendo descartados, mas o lado do cliente não sabe nada sobre isso. Sua tentativa com echo
funcionou porque echo
nunca lê nenhuma entrada, deixa sua entrada padrão em paz.
Existem algumas maneiras de evitar isso. Você pode dizer ao ssh para não ler da entrada padrão, com a -n
opção
while read server; do
ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt
A -n
opção de fato diz ssh
para redirecionar sua entrada de /dev/null
. Você pode fazer isso no nível do shell e funcionará para qualquer comando.
while read server; do
ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt
Um método tentador entrada evitar de ssh que vem do arquivo é colocar o redirecionamento no read
comando: while read server </home/kenny/list_of_servers.txt; do …
. Isso não funcionará, pois faz com que o arquivo seja aberto novamente toda vez que o read
comando for executado (para ler a primeira linha do arquivo repetidamente). O redirecionamento precisa estar no loop while geral, para que o arquivo seja aberto uma vez durante a duração do loop.
A solução geral é fornecer a entrada para o loop em um descritor de arquivo diferente da entrada padrão. O shell possui construções para transportar entrada e saída de um número de descritor para outro. Aqui, abrimos o arquivo no descritor de arquivo 3 e redirecionamos a read
entrada padrão do comando do descritor de arquivo 3. O cliente ssh ignora os descritores não padronizados abertos, para que tudo esteja bem.
while read server <&3; do
ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt
No bash, o read
comando tem uma opção específica para ler de um descritor de arquivo diferente, para que você possa escrever read -u3 server
.
Leitura relacionada: Descritores de arquivos e scripts de shell ; Quando você usaria um descritor de arquivo adicional?
Você deveria usar
while
, nãofor
. A maneira de evitar que os comandos engolam a entrada padrão nesse loop é simplesmente usar outro descritor de arquivo :Para mais informações,
help [r]ead
( realmente ) e outro artigo explicando o porquê .fonte
-u
bandeira faz? Não sei em que página de manual devo procurar.help read
-help
é o comando para obter o equivalente deman
páginas para os componentes internos do shell.No seu primeiro código
ssh
, "roubará" o STDIN dowhile
. Adicione a-n
opção parassh
evitá-lo. Deman ssh
:fonte
for
loop recebe os dados como parâmetro, não como entrada.A manipulação de entrada padrão padrão
ssh
drena a linha restante do loop while.Para evitar esse problema, altere de onde o comando problemático lê a entrada padrão. Se nenhuma entrada padrão precisar ser passada para o comando, leia a entrada padrão do
/dev/null
dispositivo especial .fonte