SSH provoque l'arrêt de la boucle

30

J'ai finalement réussi à résumer un problème avec lequel je me bats depuis quelques semaines. J'utilise SSH avec des "clés autorisées" pour exécuter des commandes à distance. Tout va bien sauf quand je le fais en boucle. La boucle se termine après avoir terminé toute itération avec une commande ssh.

Pendant longtemps, j'ai pensé que c'était une sorte de bizarrerie ksh, mais j'ai maintenant découvert que bash se comportait en fait de manière identique.

Un petit exemple de programme pour reproduire le problème. Ceci est distillé à partir d'une implémentation plus large qui prend des instantanés et les réplique parmi les nœuds d'un cluster.

#!/bin/bash

set -x

IDTAG=".*zone"
MARKER="mark-$(date +%Y.%m.%d.%H.%M.%S)"
REMOTE_HOST=sol10-target
ZFSPARENT=rpool

ssh $REMOTE_HOST zfs list -t filesystem -rHo name,mounted $ZFSPARENT | grep "/$IDTAG    " > /tmp/actionlist

#for RMT_FILESYSTEM in $(cat /tmp/actionlist)
cat /tmp/actionlist | while read RMT_FILESYSTEM ISMOUNTED
do
   echo ${RMT_FILESYSTEM}@${MARKER}
   [ "$ISMOUNTED" = "yes" ] && ssh $REMOTE_HOST zfs snapshot -r ${RMT_FILESYSTEM}@${MARKER}
   echo Remote Command Return Code: $?
done

(Notez qu'il y a un caractère TAB dans l'expression de recherche grep selon la définition du comportement de l'option "-H" de la liste zfs.)

Mon échantillon a des systèmes de fichiers ZFS pour la racine où toutes les "zones" ont leur système de fichiers racine sur un ensemble de données nommé similaire à

PISCINE / zones / app1zone
PISCINE / zones / group2 / app2zone

etc.

La boucle ci-dessus doit créer un instantané pour chacun des ensembles de données sélectionnés, mais à la place, elle ne fonctionne que sur le premier, puis se termine.

Que le programme trouve le bon nombre d'ensembles de données peut être facilement confirmé en vérifiant le fichier "/ tmp / actionlist" après que le script existe.

Si la commande ssh est remplacée, par exemple, par une commande echo, la boucle parcourt toutes les lignes d'entrée. Ou mon préféré - ajoutez "echo" à la commande incriminée.

Si j'utilise une boucle for à la place, cela fonctionne également, mais en raison de la taille potentielle de la liste des ensembles de données, cela pourrait provoquer des problèmes avec la longueur de ligne de commande étendue maximale.

Je suis maintenant sûr à 99,999% que seules les boucles contenant des commandes ssh me posent des problèmes!

Notez que l'itération dans laquelle la commande ssh s'exécute, se termine! C'est comme si les données envoyées dans la boucle while étaient soudainement perdues ... Si les premières lignes d'entrée n'exécutent pas de commande ssh, la boucle continue jusqu'à ce qu'elle exécute réellement la commande SSH.

Sur mon ordinateur portable où je teste cela, j'ai deux machines virtuelles Solaris 10 avec seulement deux ou trois exemples de jeux de données, mais la même chose se produit sur les grands systèmes SPARC où cela est censé être mis en ligne, et il existe de nombreux jeux de données.

Johan
la source
7
SSH peut lire à partir d'une entrée standard, manger votre actionlist. Essayez de rediriger l'entrée standard de ssh vers/dev/null
BatchyX
Je vais essayer ça. Je veux ajouter que mettre ssh dans un wrapper n'aide pas ...
Johan
Vous avez raison. Comment pourrais-je ne pas voir ça!?
Johan
@BatchyX Je pense que votre commentaire est considéré comme une réponse.
un CVn du
J'accepte que je souhaite «accepter» la réponse, donc si @BatchyX pourrait la republier en tant que telle, je le ferai.
Johan

Réponses:

43

SSH peut lire à partir d'une entrée standard, consommer votre liste d'actions. Essayez de rediriger l'entrée standard de ssh vers / dev / null:

ssh $REMOTE_HOST zfs snapshot -r ${RMT_FILESYSTEM}@${MARKER} </dev/null

En règle générale, lorsque j'exécute des commandes qui peuvent interférer avec l'entrée standard sous une while readboucle de style, j'aime encapsuler tout le corps de la boucle entre accolades:

cat /tmp/uuoc | while read RMT_FILESYSTEM ISMOUNTED
do {
    echo ${RMT_FILESYSTEM}@${MARKER}
    [ "$ISMOUNTED" = "yes" ] && ssh $REMOTE_HOST zfs snapshot -r ${RMT_FILESYSTEM}@${MARKER}
    echo Remote Command Return Code: $?
} < /dev/null; done
BatchyX
la source
3
Double impressionnant pour tout d'abord l'utilisation spécifique des accolades ... que je n'ai jamais rencontré en 20 ans de script (du moins pas que je me souvienne.) Et pour l'inférence uuoc. FWIW (et pour ma propre défense) Je fais parfois une exception et j'ajoute des déclarations redondantes pour des raisons de lisibilité! J'adore ce forum car j'apprends soudain de nouvelles choses! Plus précisément, j'aime ajouter des redirections au début des lignes comme dans ce cas, mais sur les forums qui semblent embrouiller la question, ce qui me fait obtenir moins de réponses utiles!
Johan
J'ai essayé de faire la différence entre {...} et (...) la page de manuel de ksh indique que {...} sont des mots-clés spéciaux reconnus uniquement au début d'une ligne de commande. Mais il y a une autre différence ... ( </tmp/file [ -z "$SOMEVAR" ] && awk '{print "X", $0}' )diffère de la même chose avec les accolades. Je veux dire en termes de sortie produite, pas sur le fait que l'accolade fermante doit être sur une nouvelle ligne ...
Johan
5
De plus, ssh d'OpenSSH a l' -noption qui lui fait (effectivement) rouvrir son stdin depuis / dev / null.
Chris Johnsen
Des solutions de contournement pour utiliser sudoavec la commande à l'intérieur de la boucle?
bonh
J'avais l'habitude d'utiliser -n depuis des lustres et à un moment donné j'ai perdu l'habitude .... et maintenant je sais à nouveau pourquoi je l'ai fait, après avoir passé un peu de temps à creuser ...
Florian Heigl