O que há de errado com esse loop while?

4

Plano de fundo

Eu criei um script para:

  • Leia uma lista de um ip de cada vez
  • Copiar um arquivo de configuração do host local para o host remoto
  • Reinicie o host remoto
  • Fechar a sessão ssh atual

Conteúdo do script:

#!/bin/bash

SSHPASS="OMITTED"
FILE_TO_COPY=/opt/someConfigFile.conf
TARGET_FOLDER=/opt/

echo "Reading ip list..."
cat $1 | while read ip
do
    echo "Copying file to $ip"
    sshpass -p $SSHPASS scp -o StrictHostKeyChecking=no -o ServerAliveInterval=3 $FILE_TO_COPY root@$ip:$TARGET_FOLDER
    echo "Sending reboot command to $ip"
    sshpass -p $SSHPASS ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=3 'nohup reboot > /dev/null & exit'
    echo "Done for $ip"
done
echo "Done for all"

Meu script lê entradas de um arquivo de texto em que cada entrada é separada por uma nova linha, algo como:

192.168.XXX.XX1
192.168.XXX.XX2
192.168.XXX.XX3
192.168.XXX.XX4

Quando executo esse script, ./ConfigSender.sh /host_list.txt , posso ver a seguinte saída:

$> ./ConfigSender.sh /host_list.txt
Rading ip list...
Copying file to 192.168.XXX.XX1
Sending reboot command to 192.168.XXX.XX1
Done for 192.168.XXX.XX1
Done for all
$>

Eu esperava ver a saída de todas as entradas no arquivo. Eu suspeitei que os comandos dentro de while de alguma forma quebraram a execução. Então eu editei meu script para apenas imprimir o valor do ip de leitura para a saída:

#...
echo "Reading ip list..."
cat $1 | while read ip
do
    echo "Copying file to $ip"
done
echo "Done for all"

Esta é a saída:

$> ./ConfigSender.sh /host_list.txt
Rading ip list...
Copying file to 192.168.XXX.XX1
Copying file to 192.168.XXX.XX2
Copying file to 192.168.XXX.XX3
Copying file to 192.168.XXX.XX4
Done for all
$>

Pergunta

É claro que os comandos dentro do original while causam esse comportamento. O que há de errado com o loop while? O que estou perdendo aqui?

EDITAR

Ambos @harrymc & @Kamil Maciorowski respostas estão resolvendo meu problema, muito obrigado. Decidi aceitar a resposta do @ harrymc, pois é mais descritivo.

    
por raidensan 02.08.2017 / 13:27

3 respostas

3

sshpass funciona manipulando stdin para enganar ssh pensando que está obtendo a senha de um usuário interativo. Quando você usa um loop de estilo ... | while , o loop é repetido para cada linha proveniente de stdin , que sshpass apaga após a primeira chamada. É por isso que apenas a primeira linha é executada.

Várias soluções são possíveis:

  • Redirecionar para sshpass a entrada padrão para /dev/null
  • Envolva todo o corpo do loop em chaves para isolar o stdin ( {} )
  • Atribuir a uma matriz antes do loop para que você não esteja usando stdin
    readarray a < file
    for ip in "${a[@]}"; do
  • Faz loop sobre um descritor de arquivo diferente de stdin
    while read -u 5 -r ip; do
    ... e done 5<file
por 02.08.2017 / 14:09
2

Eu fiz alguns testes. Eu acho que suas linhas gostam

sshpass …

leia stdin , eles "comem" IPs adicionais fornecidos por cat . Você pode investigar por que isso acontece, mas é suficiente saber que isso acontece .

A solução é dar a eles alguma outra entrada:

</dev/null sshpass …

Ou você pode reconstruir seu loop assim:

for ip in 'cat $1'
do
 …
done
    
por 02.08.2017 / 14:05
0

Não tenho certeza se isso resolverá o problema, mas você não forneceu o nome de usuário e host na linha de comando de reinicialização ssh.

sshpass -p $SSHPASS ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=3 root@$ip 'nohup reboot > /dev/null & exit'

    
por 02.08.2017 / 13:48