Finalización del proceso Redirección de entrada de bucle infinito

Finalización del proceso Redirección de entrada de bucle infinito

Editar: me di cuenta de que el programa que estaba intentando ejecutar tenía un bucle infinito después de escanear todas mis entradas. Luego simplemente imprime infinitamente para que nunca lea el EOF. Leerá la última entrada y luego entrará en un bucle infinito.

Por ejemplo, digamos que el programa es algo como esto.

printf("%c\n", getch());
while(1)
{
   printf("%c\n", getch());
}

¿Hay alguna manera de finalizar el programa después de leer todas mis entradas? Quiero poder cerrar el programa cuando termine de redirigir el archivo de entrada. (Cuando el programa obtiene el último carácter del archivo de entrada) Nota: No puedo modificar el programa que estoy ejecutando, pero puedo modificar el script que ejecuta este programa.

Ahora tengo esto en mi script para ejecutar el programa. Básicamente, esto me ayuda a combinar/fusionar entrada + salida.

EDITAR: Alguien me ayudó a resolver mi problema anterior. Se suponía que debía agregar || Vaya a esta ruta pero tengo un nuevo problema que está en la edición en la parte superior.

mkfifo strace.fifo
{
  while read -d, trace; do
    if [[ $trace = *"read(0" ]]; then
       IFS= read -rn1 answer <&3 || break
       echo "$answer" >> in
       answer=${answer:-$'\n'}
       printf %s "$answer" >&2
       printf %s "$answer"
    fi
  done < strace.fifo 3< input | strace -o strace.fifo -e read stdbuf -o0 ./a.out &
} >> $fname.out 2>&1

Así que ahora tengo una forma realmente ingeniosa de intentar finalizar el programa usando la suspensión y matando el programa. También estoy verificando la entrada utilizada hasta ahora y comparándola con el archivo de entrada. Este es solo un pequeño ejemplo de lo que estoy haciendo. Mi script actual tiene múltiples temporizadores y comparaciones similares al código siguiente. Sin embargo, esta manera no es una buena manera porque si el programa no termina de leer el archivo de entrada después del segundo que ponga, no matará el programa. En mi script real utilicé varias de estas declaraciones if y funciona aproximadamente el 80% de las veces, pero a veces no hace lo correcto, probablemente debido al temporizador y la fluctuación en la forma en que se ejecuta el programa.

sleep .05
awk 'BEGIN{ORS="";} NR==1{print; next;} /^[^ ]/ { print; next; } {print "\n";}' in > temp
diff -w -B temp input > /dev/null
# Check if input done yet
if [ $? -eq 0 ] ; then
   # Check if program still running and input done
   if [ "$(pidof a.out)" ] ; then
      killall -15 a.out > /dev/null 2>&1
   fi
fi

Entonces me pregunto si hay alguna forma de realizar killel proceso stracedespués de realizar la entrada. También quiero poder mantener la entrada y la salida fusionadas.

Respuesta1

Creo que tengo una solución mucho más sencilla, pero no estoy seguro de qué tan portátil sea. Considerar:

$ cat trunc.c
#include <stdio.h>

int main() {
    int c;
    while(((c = getchar()) != EOF) && ((c & 0xff) != 0xff))
        putchar(c);
    return 0; 
}

Esto aprovecha el hecho de que en una máquina en complemento a dos (-1 & 0xff) == 0xff. Esto captura su programa que imprime EOF como carácter. Lo probé:

your_buggy_source_gen < any_ascii_file | trunc

lo que equivale a cat any_ascii_filesiempre que no contenga octetos de valor 0xff.

Respuesta2

"Ahora me pregunto si hay alguna forma de comprobar si el programa entra en un bucle infinito antes de leer todas las entradas. Por ejemplo, si un programa entra en un bucle infinito, es la única forma de detenerlo".

Hemos conocido la respuesta aesa pregunta durante casi ochenta años. La respuesta es no, no hay manera.

Gracias por dar una pregunta que ni siquiera se acercaba a su pregunta real:

"Estoy tratando de calificar el resultado de los estudiantes. Sólo quiero crear un guión para poder usarlo en el futuro. Ahora mismo lo tengo funcionando bastante bien usando un temporizador de apagado, pero quiero que el guión sea más sólido sin un temporizador".

Solución simple:

# how many seconds of CPU (not wall clock)
# should the student's program get?
# 10 seconds is generous for an intro class
# 60 seconds of CPU would be obscene
$ ulimit -t 10
$ run_student_program

# but since I don't have a student program

$ time dd if=/dev/zero of=/dev/null
Killed

real    0m9.998s
user    0m3.080s
sys     0m6.912s

Respuesta3

No es exactamente un consejo de programación de shell, pero la realidad es que necesita modificar el comportamiento del programa roto incluso si no puede modificar su código fuente.

Una forma de hacerlo sería un módulo LD_PRELOAD que intercepte la llamada al sistema "read()", de una manera que invoque la llamada al sistema "read()" real y luego salga cuando encuentre una condición de fin de archivo. (Lo ideal sería interceptar "getchar()" pero normalmente es una macro en lugar de una llamada de función, por lo que no se puede interceptar).

Otra forma sería ejecutar el código en un depurador, encontrar dónde entra en el bucle infinito y luego parchear el binario, ya sea modificando el archivo ejecutable o nuevamente aplicando un módulo LD_PRELOAD que reemplaza la función errónea.

O podría hacer lo que sugiere la respuesta anterior y canalizar la salida a algo que cierre su entrada cuando el programa errante genere algo que indique que ha llegado al final de su entrada. (Tenga en cuenta que esto depende de ellonoatrapando SIGPIPE, lo cual es una suposición justa en este caso).

Una versión más sofisticada de esto sería compartir el descriptor del archivo de entrada con otro programa que no lea pero monitoree para ver si ha llegado a su fin. Sin embargo, esto es algo particularmente complicado de hacer, especialmente si se trata de tuberías, enchufes o dispositivos.

Suponiendo que su programa erróneo esté leyendo desde la entrada estándar, envuélvalo con algo como esto:

  #!/bin/sh
  ejecutar-programa incorrecto "$@" &
  pid=$!
  perl -MFcntl=:modo -e '
    @S = stat STDIN o die "Entrada no válida; $!\n";
    S_IFREG($S[2]) o die "La entrada no es un archivo simple\n";
    mientras (sysseek(STDIN,0,1) != $S[7]) { dormir 1 }
  '
  matar $pid
  esperar

Respuesta4

Realmente deberías arreglar el programa roto, pero si no puedes, puedes solucionarlo haciendo que el shell capture el archivo y lo canalice al programa, luego duerma durante uno o dos segundos antes de salir, cerrando así el canal y matando el programa con SIGPIPE.

(cat file ; sleep 2) | stupid_program

información relacionada