Salida de tubería de un programa segfaulting

13

Tengo un script que llama a un programa (específicamente, ttf2afmparte de tetex 3.0) que a veces se daña y otras no. La información que necesito siempre se imprime antes de que ocurra de manera predeterminada, pero me resulta difícil evitar que la redirección de la tubería falle y no envíe nada a la tubería cuando el programa falla.

Intenté redirigir a través de un FIFO, paréntesis del proceso con un trueal final, ejecutar desde una función de shell y encapsular sh -c, pero el script nunca parece permitir que el proceso genere nada , redirigido o de otro modo, ni siquiera para stderr.

Sé que es capaz de dar salida, ya que es perfectamente capaz de darlo desde la línea de comandos, pero no desde un script por alguna razón.

Mi pregunta es, ¿hay alguna forma de que el script ignore el hecho de que el programa falla y me da la salida de todos modos?

Estoy ejecutando BASH 4.1.10 (2) -lanzamiento.

anfetamaquina
fuente

Respuestas:

12

Los programas suelen amortiguar su producción para la eficiencia. Es decir, acumulan la salida en un área de memoria (llamada un búfer), y en realidad obtienen la salida solo cuando el búfer está lleno o en ciertos puntos clave del programa. Cuando el programa finaliza normalmente, vacía el búfer de salida (es decir, imprime los datos que quedan en él). Cuando se produce una falla, el contenido del búfer se pierde.

No observa este efecto cuando ejecuta el programa directamente en un terminal porque el comportamiento es diferente cuando la salida del programa está conectada a un terminal (a diferencia de un archivo normal o una tubería). En un terminal, el comportamiento predeterminado es vaciar el búfer al final de cada línea. Por lo tanto, verá cada línea completa que se produce hasta el punto en que el programa falla.

Puede forzar que el programa se ejecute en una terminal y recopilar su salida. La forma más simple es correr script. Hay una serie de molestias que deberá evitar:

  • script agrega una línea de encabezado al archivo de transcripción, que deberá eliminar después.
  • script no devuelve el código de estado del comando, por lo que deberá guardarlo en algún lugar si desea conocer la segfault o cualquier otro error.
  • scriptcausará salida normal y error fuera; será mejor que guarde la salida del error en un archivo separado.
export FONT="foo"
script -q -c '
    ttf2afm "$FONT.ttf" 2>"$FONT.ttf2afm-err";
    echo $? >"$FONT.ttf2afm-status"
' "$FONT.ttf2afm-typescript"
tail -n +2 <"$FONT.ttf2afm-typescript" >"foo.afm"
rm "$FONT.ttf2afm-typescript"
if [ "$(cat "$FONT.ttf2afm-status")" -ne 0 ]; then
  echo 1>&2 "Warning: ttf2afm failed"
  cat "$FONT.ttf2afm-err"
fi
Gilles 'SO- deja de ser malvado'
fuente
¿No hay una solución más elegante, como una configuración de shell que establecerá el búfer de salida en 0 o algo así?
anfetamáquina
4

Finalmente lo descubrí a través de un proceso de prueba y error. La solución es algo complicada:

(trap 'true' ERR; exec ttf2afm "$FONT") |
grep ...

Aparentemente, las execcausas ttf2afmpara hacerse cargo del proceso de subshell con el error atrapado, lo que hace que opere en un entorno en el que no importa si falla.

Capturar la ERRseñal de todo incluido detendrá la muerte del subshell y enviará una señal al script principal, que terminará inmediatamente si lo hace, cuando el programa falle.

El único problema es que el núcleo en sí mismo generará una gran cantidad de basura de seguimiento de pila directamente al dispositivo de consola una vez que el proceso falla, por lo que no hay forma de evitar que salga [que yo sepa], pero eso no importa ya que no afecta a stdout o stderr.

anfetamaquina
fuente
3
Me alegra que esto funcione para usted, pero puedo afirmar con confianza que la razón por la que funciona no es porque bash está configurando el tamaño del búfer de salida a 0. Bash no puede influir en el búfer utilizado ttf2afmdirectamente. Me pregunto cómo se las (trap true ERR; exec ttf2afm "$FONT")| …arregla para comportarse de manera diferente ttf2afm "$FONT" | ….
Gilles 'SO- deja de ser malvado'