Finalizando o redirecionamento de entrada de loop infinito do processo

Finalizando o redirecionamento de entrada de loop infinito do processo

Editar: percebi que o programa que eu estava tentando executar tinha um loop infinito depois de verificar todas as minhas entradas. Depois é só imprimir infinitamente para nunca ler o EOF. Ele lerá a última entrada e entrará em um loop infinito

Por exemplo, digamos que o programa seja algo assim.

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

Existe uma maneira de encerrar o programa depois de ler todas as minhas entradas? Quero poder encerrar o programa quando ele terminar de redirecionar o arquivo de entrada. (Quando o programa obtém o último caractere do arquivo de entrada) Nota: não posso modificar o programa que estou executando, mas posso modificar o script que executa este programa.

No momento, tenho isso em meu script para executar o programa. Basicamente, isso me ajuda a combinar/mesclar entrada + saída.

EDIT: Alguém me ajudou a resolver meu problema anterior. Eu deveria adicionar || quebrar esse strace, mas tenho um novo problema que está na edição no topo.

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

Então, agora eu tenho uma maneira realmente hackeada de tentar encerrar o programa usando sleep e encerrando o programa. Também estou verificando a entrada usada até agora e comparando-a com o arquivo de entrada. Este é apenas um pequeno exemplo do que estou fazendo, meu script atual tem vários temporizadores e comparações semelhantes ao código abaixo. No entanto, esta forma não é uma boa maneira, porque se o programa não terminar de ler o arquivo de entrada após qualquer segundo que eu coloquei, ele não matará o programa. No meu script real, usei várias dessas instruções if e funciona 80% do tempo, mas às vezes não funciona corretamente, provavelmente por causa do cronômetro e da flutuação na maneira como o programa é executado.

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

Então, estou me perguntando se existe uma maneira de fazer killo processo straceapós a entrada ser concluída? Também quero poder manter a entrada e a saída mescladas.

Responder1

Acho que tenho uma solução muito mais fácil, mas não tenho certeza de quão portátil ela é. Considerar:

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

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

Isso explora o fato de que em uma máquina com complemento de dois (-1 & 0xff) == 0xff. Isso captura seu programa que imprime EOF como um caractere. Eu testei:

your_buggy_source_gen < any_ascii_file | trunc

o que é equivalente a cat any_ascii_filedesde que não haja octetos de valor 0xff nele.

Responder2

"Agora estou me perguntando se existe uma maneira de verificar se o programa entra em um loop infinito antes que toda a entrada seja lida. Por exemplo, se um programa entra em um loop infinito, é a única maneira de interrompê-lo."

Nós sabemos a resposta paraessa pergunta por quase oitenta anos. A resposta é não, não há jeito.

Obrigado por fazer uma pergunta que não chegava nem perto da sua pergunta real:

"Estou tentando avaliar o resultado do aluno. Só quero criar um script para poder usar no futuro. No momento, estou funcionando muito bem usando um temporizador, mas quero tornar o script mais robusto sem um temporizador"

Solução simples:

# 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

Responder3

Não é exatamente uma dica de programação shell, mas a realidade é que você precisa modificar o comportamento do programa quebrado, mesmo que não consiga modificar seu código-fonte.

Uma maneira de fazer isso seria um módulo LD_PRELOAD que intercepta a chamada do sistema "read()", de uma forma que invoca a chamada do sistema "read()" real e então sai quando encontra uma condição de fim de arquivo. (Idealmente eu interceptaria "getchar ()", mas geralmente é uma macro em vez de uma chamada de função, portanto não pode ser interceptada.)

Outra maneira seria executar o código em um depurador, descobrir onde ele entra no loop infinito e, em seguida, corrigir o binário, modificando o arquivo executável ou, novamente, aplicando um módulo LD_PRELOAD que substitui a função errante.

Ou você pode fazer como a resposta anterior sugere e canalizar a saída para algo que feche sua entrada quando o programa errante gerar algo que indique que atingiu o final de sua entrada. (Observe que isso depende dissonãocapturando SIGPIPE, o que é uma suposição justa neste caso.)

Uma versão mais sofisticada disso seria compartilhar o descritor de arquivo de entrada com outro programa que não o leia, mas monitore para ver se ele atingiu seu fim. No entanto, isso é algo particularmente complicado de fazer, especialmente se você estiver lidando com canos, tomadas ou dispositivos.

Supondo que seu programa incorreto esteja lendo stdin, envolva-o com algo assim:

  #!/bin/sh
  executar-programa-ruim "$@" &
  pid=$!
  perl -MFcntl=:modo -e '
    @S = stat STDIN ou die "Entrada inválida; $!\n";
    S_IFREG($S[2]) ou die "A entrada não é um arquivo simples\n";
    enquanto (sysseek(STDIN,0,1) != $S[7]) { dormir 1 }
  '
  matar $pid
  espere

Responder4

Você realmente deveria consertar o programa quebrado, mas se não puder, você pode contornar isso fazendo com que o shell cat o arquivo e canalize-o para o programa, depois durma por um ou dois segundos antes de sair, fechando assim o canal e matando o programa com SIGPIPE.

(cat file ; sleep 2) | stupid_program

informação relacionada