J'ai un fichier servers.txt
, avec une liste de serveurs:
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
Quand je lis le fichier ligne par ligne avec while
chaque ligne et que je les répète , tout fonctionne comme prévu. Toutes les lignes sont imprimées.
$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com
Cependant, lorsque je veux ssh sur tous les serveurs et exécuter une commande, ma while
boucle cesse de fonctionner:
$ while read HOST ; do ssh $HOST "uname -a" ; done < servers.txt
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Cela se connecte uniquement au premier serveur de la liste, pas à tous. Je ne comprends pas ce qui se passe ici. Quelqu'un peut-il s'il vous plaît expliquer?
C'est encore plus étrange, car utiliser la for
boucle fonctionne bien:
$ for HOST in $(cat servers.txt ) ; do ssh $HOST "uname -a" ; done
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server2 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server3 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Cela doit être spécifique ssh
, car les autres commandes fonctionnent bien, telles que ping
:
$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt
ssh
scripting
io-redirection
Martin Vegter
la source
la source
ansible
Réponses:
ssh
lit le reste de votre entrée standard.read
lit de stdin. Les<
redirections stdin à partir d'un fichier.Malheureusement, la commande que vous essayez d'exécuter lit également stdin, elle finit donc par manger le reste de votre fichier. Vous pouvez le voir clairement avec:
Remarquez comment ont
cat
mangé (et repris) les deux lignes restantes. (Si lu l'avait fait comme prévu, chaque ligne aurait le "début" et le "fin" autour de l'hôte.)Pourquoi ça
for
marche?Votre
for
ligne ne redirige pas vers stdin. (En fait, il lit tout le contenu duservers.txt
fichier en mémoire avant la première itération). Alorsssh
continue à lire son stdin à partir du terminal (ou éventuellement rien, en fonction de la façon dont votre script est appelé).Solution
Au moins en bash, vous pouvez
read
utiliser un descripteur de fichier différent.devrait fonctionner.
10
est juste un numéro de fichier arbitraire que j'ai choisi. 0, 1 et 2 ont des significations définies et l'ouverture des fichiers commence généralement par le premier nombre disponible (donc 3 est le prochain à être utilisé). 10 est donc assez élevé pour rester à l'écart, mais suffisamment bas pour rester sous la limite dans certains obus. En plus, c'est un joli chiffre rond ...Solution alternative 1: -n
Comme McNisse l'a souligné dans sa réponse , le client OpenSSH dispose d'une
-n
option qui l'empêche de lire stdin. Cela fonctionne bien dans le cas particulier dessh
, mais bien sûr, d'autres commandes peuvent ne pas en bénéficier - les autres solutions fonctionnent quelle que soit la commande qui consomme votre stdin.Solution alternative 2: deuxième redirection
Vous pouvez apparemment (comme dans, je l'ai essayé, cela fonctionne dans ma version de Bash au moins ...) faire une deuxième redirection, qui ressemble à ceci:
Vous pouvez l'utiliser avec n'importe quelle commande, mais ce sera difficile si vous voulez réellement que le terminal entre dans la commande.
la source
10
?exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&-
cela fait du descripteur de fichier 3 une sauvegarde du stdin d'origine, puis l'utilise pour le stdin de ssh, puis ferme le FD de sauvegarde une fois l'opération terminée. Cela fonctionne sur n’importe quel shell compatible POSIX.-u
option n'est pas supportée par POSIX et ne doit donc pas être utilisée pour les#!/bin/sh
scripts. utiliser à laread HOST <&10
place. De plus, POSIX exige seulement que les shells prennent en charge les descripteurs de fichier compris entre 0 et 9; ils10<servers.txt
ne peuvent donc pas être utilisés si le script doit être strictement conforme.Comme décrit derobert
ssh
lit votrestdin
.Pour changer ce comportement, vous pouvez ajouter -n no ssh pour l'empêcher de lire stdin.
la source
En fait, il vaudrait probablement mieux utiliser pssh à partir du projet parallel-ssh .
-i
signifie interactif. pssh est également livré avec un scp parallèle et un rsync parallèle. Ce qui est bien, c’est qu’il fonctionne de manière asynchrone et exécutera autant de threads que vous le lui demandez. La valeur par défaut (non -i / interactive) est la sortie dans des répertoires séparés pour stdout / stderr, ce qu'il fait par $ outputdir / $ nom_hôte.la source
Si vous rencontrez souvent ce genre de tâche, essayez Fabric.
Installez le tissu en suivant les instructions , la plupart des cas dont vous avez besoin
sudo apt-get install fabric
Créez un fichier nommé
fabfile.py
avec le code suivant:Ensuite, exécutez
fab mytask
vous donnera le résultat que vous voulez.la source
C'est plus facile en utilisant une commande comme celle-ci:
Je fais habituellement comme ça:
Le
echo
est de voir quel serveur est bloqué, ou ne peut pas s'y connecter.la source
Parce qu'un commnand ssh prend tout le flux de l'entrée standard, alimenté par l'instruction while
Vous pouvez utiliser un tube pour changer le stdin de ssh en une autre source:
exemple :
L'entrée stdin de toutes les commandes ssh d'une boucle while doit être commutée sur une autre source.
la source