편집: 내가 실행하려고 했던 프로그램에 모든 입력을 스캔한 후 무한 루프가 발생했다는 것을 깨달았습니다. 그런 다음 EOF를 읽지 않도록 무한히 인쇄하십시오. 마지막 입력을 읽은 다음 무한 루프에 들어갑니다.
예를 들어 프로그램이 다음과 같다고 가정해 보겠습니다.
printf("%c\n", getch());
while(1)
{
printf("%c\n", getch());
}
입력 내용을 모두 읽은 후 프로그램을 종료할 수 있는 방법이 있습니까? 입력 파일 리디렉션이 완료되면 프로그램을 종료할 수 있기를 원합니다. (프로그램이 입력 파일에서 마지막 문자를 가져올 때) 참고: 실행 중인 프로그램을 수정할 수는 없지만 프로그램을 실행하는 스크립트는 수정할 수 있습니다.
이제 프로그램을 실행하기 위한 스크립트가 생겼습니다. 기본적으로 이는 입력과 출력을 결합/병합하는 데 도움이 됩니다.
편집: 누군가가 이전 문제를 해결하는 데 도움을 줬습니다. 이 추적을 깨기 위해 ||를 추가해야 했지만 상단의 편집에서 새로운 문제가 발생했습니다.
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
이제 저는 절전 모드를 사용하고 프로그램을 종료하여 프로그램을 종료하려고 시도하는 매우 이상한 방법을 갖게 되었습니다. 그리고 지금까지 사용된 입력을 확인해서 입력파일과 비교하고 있습니다. 이것은 제가 하고 있는 작업의 작은 예일 뿐입니다. 현재 스크립트에는 여러 개의 타이머가 있으며 아래 코드와 비교적 유사합니다. 그러나 이 접근 방식은 입력이 끝난 후 입력 파일 읽기가 완료되지 않으면 프로그램을 종료하지 않기 때문에 좋은 접근 방식이 아닙니다. 내 실제 스크립트에서는 이와 같은 여러 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;
}
이는 2의 보수 기계에서 이라는 사실을 활용합니다 (-1 & 0xff) == 0xff
. 이는 EOF를 문자로 인쇄하는 프로그램을 포착합니다. 나는 테스트를 했다:
your_buggy_source_gen < any_ascii_file | trunc
cat any_ascii_file
이는 값이 0xff인 옥텟이 없다고 가정하는 것과 같습니다 .
답변2
"이제 입력을 모두 읽기 전에 프로그램이 무한 루프에 들어가는지 확인할 수 있는 방법이 있는지 궁금합니다. 예를 들어 프로그램이 무한 루프에 빠지면 이것이 프로그램을 중지할 수 있는 유일한 방법입니다."
우리는 이미 답을 알고 있습니다이 질문은 거의 80년 동안 제기되어 왔습니다.. 대답은 '아니오'입니다. 방법이 없습니다.
실제 문제와는 거리가 먼 질문을 해주셔서 감사합니다.
"학생들의 결과물을 평가하려고 합니다. 나중에 사용할 스크립트를 만들고 싶습니다. 지금은 취침 타이머를 사용해도 잘 작동하지만 타이머 없이 스크립트를 더욱 강력하게 만들고 싶습니다."
간단한 솔루션:
# 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
정확히 쉘 프로그래밍 트릭은 아니지만 현실은 소스 코드를 수정할 수 없더라도 손상된 프로그램의 동작을 수정해야 한다는 것입니다.
한 가지 접근 방식은 "read()" 시스템 호출을 가로채서 실제 "read()" 시스템 호출을 호출한 다음 파일 끝 조건이 발생하면 종료되는 LD_PRELOAD 모듈을 사용하는 것입니다. (이상적으로는 "getchar()"를 가로채지만 이는 일반적으로 함수 호출이 아닌 매크로이므로 가로챌 수 없습니다.)
또 다른 접근 방식은 디버거에서 코드를 실행하고 무한 루프에 들어가는 위치를 찾은 다음 실행 파일을 수정하거나 잘못된 기능을 대체하는 LD_PRELOAD 모듈을 다시 적용하여 바이너리를 패치하는 것입니다.
또는 이전 답변에서 제안한 대로 오류 프로그램 출력이 입력 끝에 도달했음을 나타낼 때 입력을 닫는 항목으로 출력을 파이프할 수 있습니다. (이것은 다음에 의존합니다.아니요이 경우 합리적인 가정인 SIGPIPE를 캡처합니다. )
보다 정교한 버전은 입력 파일 설명자를 읽지 않지만 끝에 도달했는지 모니터링하는 다른 프로그램과 공유하는 것입니다. 그러나 이는 특히 파이프, 소켓 또는 장치를 다루는 경우 까다로운 작업입니다.
잘못된 프로그램이 stdin에서 읽는다고 가정하고 다음과 같이 래핑하세요.
#!/bin/sh 실행 중 오류 프로그램 "$@"& PID=$! perl -MFcntl=:mode-e ' @S = stat STDIN 또는 die "잘못된 입력; $!\n"; S_IFREG($S[2]) 또는 die "입력이 일반 파일이 아닙니다\n"; while (sysseek(STDIN,0,1) != $S[7]) { 수면 1 } ' $pid를 죽여라 기다리다
답변4
실제로 손상된 프로그램을 수정해야 하지만 그렇게 할 수 없는 경우 쉘을 사용하여 파일을 처리하고 프로그램에 파이프한 다음 종료하기 전에 1~2초 동안 잠자기하여 파이프를 닫고 다음을 사용하여 종료함으로써 문제를 해결할 수 있습니다. SIGPIPE 프로그램.
(cat file ; sleep 2) | stupid_program