Leer la entrada del usuario dentro de un bucle

99

Tengo un script bash que es algo así como seguir,

cat filename | while read line
do
    read input;
    echo $input;
done

pero esto claramente no me da la salida correcta, ya que cuando leo en el ciclo while, intenta leer desde el nombre del archivo debido a la posible redirección de E / S.

¿Alguna otra forma de hacer lo mismo?

w2lame
fuente
Lo mismo sucede cuando cambia de usuario en bash y ejecuta el comando de lectura bajo el usuario cambiado en el script
krupal

Respuestas:

106

Leer desde el dispositivo terminal de control:

read input </dev/tty

más información: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop

húmedo
fuente
12
-1, ya que evitará cualquier otra redirección. Por ejemplo, bash yourscript < /foo/baresperará la entrada del usuario, esto es aceptable solo al leer contraseñas. La respuesta de @GordonDavisson es preferible para todos los demás usos.
tiwo
56

Puede redirigir el stdin regular a través de la unidad 3 para mantenerlo dentro de la tubería:

{ cat notify-finished | while read line; do
    read -u 3 input
    echo "$input"
done; } 3<&0

Por cierto, si realmente lo está utilizando de catesta manera, reemplácelo con una redirección y las cosas se volverán aún más fáciles:

while read line; do
    read -u 3 input
    echo "$input"
done 3<&0 <notify-finished

O bien, puede intercambiar stdin y la unidad 3 en esa versión: lea el archivo con la unidad 3 y simplemente deje stdin solo:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished
Gordon Davisson
fuente
¿Por qué cuelga tu segundo guión?
Luca Borrione
2
@LucaBorrione: ¿Cómo lo estás usando? ¿Está esperando que le dé una entrada (tenga en cuenta que read lineestá leyendo de notificar finalizado, pero si simplemente lo ejecuta como read -u 3 inputestá escrito, está leyendo desde la consola)?
Gordon Davisson
3

Parece que ha leído dos veces, la lectura dentro del ciclo while no es necesaria. Además, no necesita invocar el comando cat:

while read input
do
    echo $input
done < filename
Hai Vu
fuente
4
El objetivo del OP es que la lectura dentro del bucle provenga del usuario, mientras que la externa es la lectura del archivo. Por lo tanto, quieren legítimamente dos lecturas diferentes, de dos fuentes diferentes. Esto queda claro tanto por el texto de la pregunta (que describe el readcomportamiento del interior como "incorrecto [porque] intenta leer del archivo filename") y su respuesta aceptada.
Charles Duffy
3

Intenta cambiar el bucle así:

for line in $(cat filename); do
    read input
    echo $input;
done

Prueba de unidad:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done
dimba
fuente
@ w2lame Probado de nuevo, cambie el bucle "while" por el bucle "for" - funciona para mí. Pruebe "sex -x" para ver de dónde viene el error
dimba
4
no use gato, vea la respuesta de Hai Vu
Fredrik Pihl
+1. Esto fue mucho más fácil de implementar para mis necesidades específicas que otras sugerencias.
Nathan Wallace
1
Consulte No leer líneas con para en la wiki de Wooledge. Además, shellcheck.net advertencia SC2013
Charles Duffy
1

Encontré este parámetro -u con read.

"-u 1" significa "leer desde stdin"

while read -r newline; do
    ((i++))
    read -u 1 -p "Doing $i""th file, called $newline. Write your answer and press Enter!"
    echo "Processing $newline with $REPLY" # united input from two different read commands.
done <<< $(ls)
xerostomus
fuente
-6
echo "Enter the Programs you want to run:"
> ${PROGRAM_LIST}
while read PROGRAM_ENTRY
do
   if [ ! -s ${PROGRAM_ENTRY} ]
   then
      echo ${PROGRAM_ENTRY} >> ${PROGRAM_LIST}
   else
      break
   fi
done
Munchk1n
fuente