GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué no sale un ciclo bash while cuando se conecta a un subcomando terminado?

Se debe a una elección en la implementación.

Ejecutando el mismo script en Solaris con ksh93 produce un comportamiento diferente:

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]

Lo que desencadena el problema es la canalización interna, sin ella, el ciclo sale del shell/OS:

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$

cat está recibiendo una señal SIGPIPE bajo bash pero el shell está iterando el ciclo de todos modos.

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

Golpe la documentación dice:

El shell espera todos los comandos en la canalización para terminar antes de devolver un valor.

Ksh la documentación dice:

Cada comando, excepto posiblemente el último, se ejecuta como un proceso separado; el shell espera el último comando para terminar.

POSIX estados:

Si la canalización no está en segundo plano (consulte Listas asíncronas), el shell deberá esperar el último comando especificado en la canalización para completar, y también puede esperar a que se completen todos los comandos para completar.


Este problema me ha molestado durante años. Gracias a jilliagre por el empujón en la dirección correcta.

Reiterando un poco la pregunta, en mi caja de Linux, esto se cierra como se esperaba:

while true ; do echo "ok"; done | head

Pero si agrego una tubería, no salir como se esperaba:

while true ; do echo "ok" | cat; done | head

Eso me frustró durante años. Al considerar la respuesta escrita por jilliagre, se me ocurrió esta solución maravillosa:

while true ; do echo "ok" | cat || exit; done | head

QED ...

Bueno, no del todo. Aquí hay algo un poco más complicado:

i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' || exit
done | head

Esto no funciona bien. Agregué el || exit por lo que sabe cómo terminar antes, pero el primer echo no coincide con el grep por lo que el ciclo se cierra de inmediato. En este caso, realmente no está interesado en el estado de salida del grep . Mi solución es agregar otro cat . Entonces, aquí hay un guión artificial llamado "decenas":

#!/bin/bash
i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' | cat || exit
done

Esto termina correctamente cuando se ejecuta como tens | head . Gracias a Dios.


Linux
  1. ¿Por qué se produce un error en el aviso de Bash cuando examino el historial?

  2. ¿Código de salida predeterminado cuando finaliza el proceso?

  3. ¿Por qué el script de Bash no se cierra después de la ejecución?

  4. Ejemplos de ciclos for y while de Bash

  5. Manteniéndote al tanto – Bash For, While, Until Ejemplos de bucles

Bash break:cómo salir de un bucle

Golpear mientras se repite

Golpear hasta bucle

Secuencias de comandos de Bash:ciclo while y till explicado con ejemplos

Bash:Bucle hasta que el estado de salida del comando sea igual a 0

Shell Script while bucle:[alrededor de una canalización que falta `]'