Script shell pendant que la boucle de lecture de la ligne s'arrête après la première ligne

107

J'ai le script shell suivant. Le but est de parcourir chaque ligne du fichier cible (dont le chemin est le paramètre d'entrée du script) et de travailler sur chaque ligne. Maintenant, il semble que cela ne fonctionne qu'avec la toute première ligne du fichier cible et s'arrête après le traitement de cette ligne. Y a-t-il un problème avec mon script?

#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets 

FILENAME=$1
count=0

echo "proceed with $FILENAME"

while read LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh $LINE
done < $FILENAME

echo "\ntotal $count targets"

Dans do_work.sh, j'exécute quelques sshcommandes.

bcbishop
la source
1
Votre script est correct, mais il y a peut-être quelque chose qui ne va pas avec le do_work.sh
sleepsort
2
Oui, il pourrait consommer toutes les entrées, ou il pourrait être appelé comme sourceet simplement quitter ou exec. Mais ce code n'a pas l'air authentique, l'OP remarquerait que l'écho nécessite -ed'afficher correctement le
saut de
3
do_work.shN'exécuter sshpar hasard?
dogbane
1
oui, do_work.sh exécute quelques commandes ssh. quelque chose de spécial à ce sujet?
bcbishop
1
Mieux vaut montrer la do_work.shsource et exécuter do.shavec set -xpour déboguer.
koola

Réponses:

178

Le problème est que do_work.shexécute des sshcommandes et sshlit par défaut à partir de stdin qui est votre fichier d'entrée. En conséquence, vous ne voyez que la première ligne traitée, car sshconsomme le reste du fichier et votre boucle while se termine.

Pour éviter cela, passez l' -noption à votre sshcommande pour la faire lire à partir de la /dev/nullplace de stdin.

dogbane
la source
1
Très utile, m'a aidé à exécuter ce oneliner zsh: hôtes de chat | pendant la lecture de l'hôte; do ssh $ host do_something; done
rat
3
@rat Vous voulez toujours éviter l' inutile cat. On pourrait penser qu'un rongeur en particulier s'en méfierait.
tripleee
while read host ; do $host do_something ; done < /etc/hostsl'éviterait. C'est tout à fait une bouée de sauvetage, merci!
rat
httpieest une autre commande qui lit STDIN par défaut, et souffrira du même comportement lorsqu'elle sera appelée dans une boucle bash ou fish. Utilisez http --ignore-stdinou réglez l'entrée standard /dev/nullcomme ci-dessus.
Raman
12

Plus généralement, une solution de contournement qui n'est pas spécifique à sshest de rediriger l'entrée standard pour toute commande qui pourrait autrement consommer l' whileentrée de la boucle.

while read -r LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh "$LINE" </dev/null
done < "$FILENAME"

L'ajout de </dev/nullest le point crucial ici (bien que les guillemets corrigés soient également quelque peu importants; voir aussi Quand enrouler les guillemets autour d'une variable shell? ). Vous voudrez l'utiliser read -rsauf si vous avez spécifiquement besoin du comportement hérité légèrement étrange dont vous vous débarrassez -r.

Une autre solution de contournement qui est quelque peu spécifique sshest de s'assurer que toute sshcommande a son entrée standard liée, par exemple en modifiant

ssh otherhost some commands here

à la place, lire les commandes d'un document ici, ce qui, de manière pratique (pour ce scénario particulier), lie l'entrée standard de sshpour les commandes:

ssh otherhost <<'____HERE'
    some commands here
____HERE
tripleee
la source
5

L'option ssh -n empêche la vérification de l'état de sortie de ssh lors de l'utilisation de HEREdoc lors du transfert de la sortie vers un autre programme. L'utilisation de / dev / null comme stdin est donc préférable.

#!/bin/bash
while read ONELINE ; do
   ssh ubuntu@host_xyz </dev/null <<EOF 2>&1 | filter_pgm 
   echo "Hi, $ONELINE. You come here often?"
   process_response_pgm 
EOF
   if [ ${PIPESTATUS[0]} -ne 0 ] ; then
      echo "aborting loop"
      exit ${PIPESTATUS[0]}
   fi
done << input_list.txt
jacobm654321
la source
Cela n'a pas de sens. Le <<EOFremplace la </dev/nullredirection. La <<redirection après le doneest fausse.
tripleee
1

Cela m'arrivait parce que j'avais set -eet un grepdans une boucle revenait sans sortie (ce qui donne un code d'erreur non nul).

JonnyRaa
la source