El bucle while deja de leer después de la primera línea en Bash

Resuelto bcbishop asked hace 12 años • 6 respuestas

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 del archivo de destino y se detiene después de que se procesó esa línea. ¿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 avatar Dec 10 '12 18:12 bcbishop
Aceptado

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

Esto sucede no solo para ssh, sino para cualquier comando que lea stdin, incluidos mplayer, ffmpeg, HandBrakeCLI, httpie, brew instally más.

Para evitar esto, pase la -nopción a su sshcomando para que se lea desde /dev/nullen lugar de stdin. Otros comandos tienen indicadores similares, o puedes usarlos universalmente < /dev/null.

dogbane avatar Dec 10 '2012 11:12 dogbane

Una solución alternativa muy simple y sólida es cambiar el descriptor de archivo desde el cual el readcomando recibe la entrada.

Esto se logra mediante dos modificaciones: el -uargumento to ready el operador de redirección for < $FILENAME.

En BASH, los valores predeterminados del descriptor de archivo (es decir, valores para -uin read) son:

  • 0 = entrada estándar
  • 1 = salida estándar
  • 2 = estándar

Así que simplemente elija algún otro descriptor de archivo no utilizado, como 9solo por diversión.

Por lo tanto, la siguiente sería la solución:

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

Observe las dos modificaciones:

  1. readse convierteread -u 9
  2. < $FILENAMEse convierte9< $FILENAME

Como práctica recomendada, hago esto para todos whilelos bucles que escribo en BASH. Si tiene bucles anidados usando read, utilice un descriptor de archivo diferente para cada uno (9,8,7,...).

cmo avatar Feb 12 '2021 21:02 cmo

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 podría consumir la whileentrada del bucle.

while read -r line; do
   ((count++))
   echo "$count $line"
   sh ./do_work.sh "$line" </dev/null
done < "$filename"

La adición de </dev/nulles el punto crucial aquí, aunque la cita corregida también es algo importante para la solidez; consulte también ¿Cuándo colocar comillas alrededor de una variable de shell? . Querrá usarlo read -ra menos que requiera específicamente el comportamiento heredado ligeramente extraño que obtiene con las barras invertidas en la entrada sin -r. Finalmente, evite las mayúsculas en sus variables privadas.

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

ssh otherhost some commands here

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

ssh otherhost <<'____HERE'
    some commands here
____HERE
tripleee avatar Mar 25 '2019 09:03 tripleee

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 entrada estándar.

#!/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 avatar Nov 14 '2015 22:11 jacobm654321