![Завершение процесса Бесконечный цикл Перенаправление ввода](https://rvso.com/image/31660/%D0%97%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%B0%20%D0%91%D0%B5%D1%81%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9%20%D1%86%D0%B8%D0%BA%D0%BB%20%D0%9F%D0%B5%D1%80%D0%B5%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0.png)
Редактировать: Я понял, что программа, которую я пытался запустить, имела бесконечный цикл после сканирования всех моих входных данных. Затем она просто печатает бесконечно, так что она никогда не прочитает 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