Quel est le problème avec cette boucle While?

3

Contexte

J'ai créé un script pour:

  • Lire une liste d'adresses IP une à la fois
  • Copier un fichier de configuration de l'hôte local vers l'hôte distant
  • Redémarrer l'hôte distant
  • Fermer la session ssh en cours

Contenu du 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"

Mon script lit les entrées d'un fichier texte dans lequel chaque entrée est séparée par une nouvelle ligne, quelque chose comme:

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

Lorsque je lance ce script, ./ConfigSender.sh /host_list.txtje peux voir la sortie suivante:

$> ./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
$>

Je m'attendais à voir la sortie pour toutes les entrées du fichier. Je soupçonnais que les commandes à l'intérieur avaient en whilequelque sorte brisé l'exécution. J'ai donc édité mon script pour simplement imprimer la valeur lue en sortie:

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

C'est la sortie:

$> ./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
$>

Question

Il est clair que les commandes à l'intérieur d'origine whilecausent ce comportement. Quel est le problème avec la boucle while? Qu'est-ce que j'oublie ici?

MODIFIER

Les réponses @harrymc & @Kamil Maciorowski résolvent mon problème, merci beaucoup. J'ai décidé d'accepter la réponse de @ harrymc car elle est plus descriptive.

raidensan
la source

Réponses:

3

sshpassfonctionne en manipulant stdinpour sshcroire qu’il obtient le mot de passe d’un utilisateur interactif. Lorsque vous utilisez une ... | whileboucle de style, la boucle itère pour chaque ligne en provenance de celle-ci stdin, qui sshpassdisparaît après le premier appel. C'est pourquoi seule la première ligne est exécutée.

Plusieurs solutions sont possibles:

  • Rediriger pour sshpass l'entrée standard vers /dev/null
  • Enroulez le corps entier de la boucle dans des accolades pour isoler stdin ( {})
  • Assigner à un tableau avant la boucle pour que vous n'utilisiez pas stdin
    readarray a < file
    for ip in "${a[@]}"; do
  • Boucle sur un descripteur de fichier autre que stdin
    while read -u 5 -r ip; do
    ...
    done 5<file
harrymc
la source
2

J'ai fait des tests. Je pense que vos lignes aiment

sshpass 

lire stdin, ils "mangent" des adresses IP supplémentaires fournies par cat. Vous pouvez enquêter sur la raison pour laquelle il arrive , mais il suffit de savoir qu'il ne se produise.

La solution consiste à leur donner une autre contribution:

</dev/null sshpass 

Ou vous pouvez reconstruire votre boucle comme ceci:

for ip in `cat $1`
do
 
done
Kamil Maciorowski
la source
0

Pas sûr que cela résoudra le problème, mais vous n’avez pas fourni l’utilisateur et le nom d’hôte dans votre ligne de commande ssh reboot.

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

Vera
la source