Завершение процесса Бесконечный цикл Перенаправление ввода

Завершение процесса Бесконечный цикл Перенаправление ввода

Редактировать: Я понял, что программа, которую я пытался запустить, имела бесконечный цикл после сканирования всех моих входных данных. Затем она просто печатает бесконечно, так что она никогда не прочитает EOF. Она прочитает последние входные данные, а затем войдет в бесконечный цикл

Например, предположим, что программа выглядит примерно так.

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

Есть ли способ завершить программу после считывания всех моих входных данных? Я хочу иметь возможность завершить программу, когда она закончит перенаправление входного файла. (Когда программа получит последний символ из входного файла) Примечание: я не могу изменить программу, которую я запускаю, но я могу изменить скрипт, который запускает эту программу.

Сейчас у меня в скрипте есть это для запуска программы. По сути это помогает мне объединять/сливать вместе ввод + вывод.

EDIT: Кто-то помог мне решить мою предыдущую проблему. Я должен был добавить || break в эту строку, но у меня появилась новая проблема, которая находится в редактировании вверху.

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

Итак, прямо сейчас у меня есть действительно хакерский способ попытаться завершить программу, используя sleep и убивая программу. Также я проверяю ввод, использованный до сих пор, и сравниваю его с входным файлом. Это всего лишь небольшой пример того, что я делаю, мой текущий скрипт имеет несколько сравнений таймера и if, похожих на код ниже. Однако этот способ не очень хорош, потому что если программа не закончит чтение входного файла после любой указанной мной секунды, это не убьет программу. В моем реальном скрипте я использовал несколько таких операторов if, и это работает примерно в 80% случаев, но иногда это не делает правильно, вероятно, из-за таймера и колебаний в способе работы программы.

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

Так что мне интересно, есть ли способ сделать killпроцесс после того, straceкак ввод сделан? Также я хочу иметь возможность сохранять ввод и вывод объединенными вместе.

решение1

Я думаю, что у меня есть гораздо более простое решение, но я не уверен, насколько оно портативно. Рассмотрим:

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

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

Это использует тот факт, что на машине с дополнительным кодом (-1 & 0xff) == 0xff. Это ловит вашу программу, которая печатает EOF как символ. Я это проверил:

your_buggy_source_gen < any_ascii_file | trunc

что эквивалентно при cat any_ascii_fileусловии, что в нем нет октетов со значением 0xff.

решение2

«Теперь мне интересно, есть ли способ проверить, не попадает ли программа в бесконечный цикл, до того, как будут прочитаны все входные данные. Например, если программа попадает в бесконечный цикл, это единственный способ остановить программу».

Мы знаем ответ на этот вопрос.этот вопрос уже почти восемьдесят лет. Ответ — нет, такого способа нет.

Спасибо, что задали вопрос, который даже близко не соответствовал вашему реальному вопросу:

«Я пытаюсь оценить работу студентов. Я просто хочу создать сценарий, который смогу использовать в будущем. Сейчас он работает довольно хорошо с использованием таймера сна, но я хочу сделать сценарий более надежным без таймера».

Простое решение:

# 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

решение3

Это не совсем совет по программированию в оболочке, но реальность такова, что вам нужно изменить поведение неисправной программы, даже если вы не можете изменить ее исходный код.

Одним из способов сделать это может быть модуль LD_PRELOAD, который перехватывает системный вызов «read()» таким образом, что он вызывает фактический системный вызов «read()», а затем завершает работу, когда обнаруживает условие конца файла. (В идеале я бы перехватил «getchar()», но это обычно макрос, а не вызов функции, поэтому его нельзя перехватить.)

Другой способ — запустить код под отладчиком, найти место, где он попадает в бесконечный цикл, а затем исправить двоичный файл, либо изменив исполняемый файл, либо снова применив модуль LD_PRELOAD, который заменяет ошибочную функцию.

Или вы можете поступить так, как предлагает предыдущий ответ, и передать вывод во что-то, что закроет его ввод, когда ошибочная программа выведет что-то, что укажет на то, что она достигла конца своего ввода. (Обратите внимание, что это зависит от этого)нет(Захват SIGPIPE, что является справедливым предположением в данном случае.)

Более сложная версия этого — поделиться входным файловым дескриптором с другой программой, которая не читает из него, а следит за тем, не достиг ли он конца. Однако это особенно сложная задача, особенно если вы имеете дело с каналами, сокетами или устройствами.

Предполагая, что ваша ошибочная программа считывает данные со стандартного ввода, оберните ее чем-то вроде этого:

  #!/bin/ш
  запустить плохую программу "$@" &
  пид=$!
  perl -MFcntl=:режим -e '
    @S = stat STDIN or die "Неверный ввод; $!\n";
    S_IFREG($S[2]) или die "Входные данные не являются простым файлом\n";
    пока (sysseek(STDIN,0,1) != $S[7]) { сон 1 }
  '
  убить $pid
  ждать

решение4

Вам действительно следует исправить сломанную программу, но если вы не можете, вы можете обойти это, заставив оболочку cat перенаправить файл в программу, а затем заснуть на секунду или две перед выходом, тем самым закрыв канал и завершив программу с помощью SIGPIPE.

(cat file ; sleep 2) | stupid_program

Связанный контент