El script de shell mientras el bucle de línea de lectura se detiene después de la primera línea

107

Tengo el siguiente script de shell. El propósito es recorrer cada línea del archivo de destino (cuya ruta es el parámetro de entrada al script) y trabajar en cada línea. Ahora, parece que solo funciona con la primera línea en el archivo de destino y se detiene después de que esa línea se procesó. ¿Hay algún problema con mi guión?

#!/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"

En do_work.sh, ejecuto un par de sshcomandos.

bcbishop
fuente
1
Su secuencia de comandos está bien, pero podría haber algún problema con do_work.sh
sleepsort
2
Sí, podría consumir toda la entrada, o podría invocarse como sourcey simplemente salir o exec. Pero este código no parece genuino, el OP notaría que el eco requiere -emostrar el avance de línea correctamente ...
Michael Krelin - hacker
3
¿ do_work.shCorre sshpor casualidad?
dogbane
1
sí, do_work.sh ejecuta un par de comandos ssh. algo especial sobre eso?
bcbishop
1
Mejor que muestran el do_work.shorigen y también ejecutar do.shcon set -xdepurar.
koola

Respuestas:

178

El problema es que do_work.shejecuta sshcomandos y por defecto sshlee desde stdin, que es su archivo de entrada. Como resultado, solo verá la primera línea procesada, porque sshconsume el resto del archivo y su ciclo while termina.

Para evitar esto, pase la -nopción a su sshcomando para que se lea desde en /dev/nulllugar de stdin.

dogbane
fuente
1
Muy útil, me ayudó a ejecutar este zsh oneliner: cat hosts | mientras lee el host; hacer ssh $ host hacer_algo; hecho
rata
3
@rat Todavía quieres evitar lo inútil cat. Uno pensaría que un roedor en particular desconfiaría de esto.
tripleee
while read host ; do $host do_something ; done < /etc/hostslo evitaría. Eso es un gran salvavidas, ¡gracias!
rata
httpiees otro comando que lee STDIN por defecto, y sufrirá el mismo comportamiento cuando sea llamado dentro de un bucle bash o fish. Use http --ignore-stdino configure la entrada estándar /dev/nullcomo arriba.
Raman
12

De manera más general, una solución alternativa que no es específica sshes redirigir la entrada estándar para cualquier comando que de otro modo consumiría la whileentrada del bucle.

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

La adición de </dev/nulles el punto crucial aquí (aunque las comillas corregidas también son algo importantes; vea también ¿ Cuándo ajustar las comillas alrededor de una variable de shell? ). Querrá usarlo a read -rmenos que requiera específicamente el comportamiento heredado ligeramente extraño que tiene sin él -r.

Otra solución alternativa que es algo específica sshes asegurarse de que cualquier sshcomando tenga su entrada estándar atada, por ejemplo, cambiando

ssh otherhost some commands here

en su lugar, leer los comandos de un documento aquí, que convenientemente (para este escenario en particular) ata la entrada estándar de ssh para los comandos:

ssh otherhost <<'____HERE'
    some commands here
____HERE
triples
fuente
5

La opción ssh -n evita verificar el estado de salida de ssh cuando se usa HEREdoc mientras se canaliza la salida a otro programa. Por lo tanto, se prefiere el uso de / dev / null como stdin.

#!/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
fuente
Esto no tiene sentido. El <<EOFanula la </dev/nullredirección. La <<redirección después de la donees incorrecta.
tripleee
1

Esto me estaba sucediendo porque tenía set -ey un grepbucle en un bucle regresaba sin salida (lo que da un código de error distinto de cero).

JonnyRaa
fuente