結束進程無限循環輸入重定向

結束進程無限循環輸入重定向

編輯:我意識到在掃描我的所有輸入後,我嘗試運行的程式有一個無限循環。然後無限地列印出來,這樣它就永遠不會讀取 EOF。它將讀取最後一個輸入,然後進入無限循環

例如,假設該程式是這樣的。

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

有沒有辦法在讀入我的所有輸入後終止程式?我希望能夠在程式完成重定向輸入檔案時終止該程式。 (當程式從輸入檔案中取得最後一個字元時)注意:我無法修改正在執行的程序,但可以修改執行該程式的腳本。

現在我的腳本中有這個來運行程式。基本上這可以幫助我將輸入+輸出組合/合併在一起。

編輯:有人幫我解決了我之前的問題。我本來應該要加||中斷此 strace,但我在頂部的編輯中遇到了一個新問題。

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; 
}

這利用了在二進制補碼機器上的事實(-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

不完全是 shell 程式設計技巧,但現實是即使您無法修改其原始程式碼,您也需要修改損壞程式的行為。

一種方法是使用 LD_PRELOAD 模組,該模組攔截“read()”系統調用,以調用實際的“read()”系統調用,然後在遇到文件結束條件時退出。 (理想情況下,我會攔截“getchar()”,但這通常是巨集而不是函數調用,因此無法被攔截。)

另一種方法是在偵錯器下運行程式碼,找到它進入無限循環的位置,然後透過修改可執行檔或再次應用替換錯誤函數的 LD_PRELOAD 模組來修補二進位。

或者,您可以按照前面的答案建議的那樣,將輸出通過管道傳輸到當錯誤程序輸出表明它已到達輸入末尾的內容時關閉其輸入的內容。 (注意,這依賴它不是捕獲 SIGPIPE,在這種情況下這是一個合理的假設。

一個更複雜的版本是與另一個程式共用輸入檔描述符,該程式不會讀取它,但監視它是否已到達末尾。然而,這是一件特別棘手的事情,特別是如果您可能正在處理管道、套接字或設備。

假設您的錯誤程式正在從 stdin 讀取數據,請用以下內容將其包裝起來:

  #!/bin/sh
  運行錯誤程式“$@”&
  PID=$!
  perl -MFcntl=:模式 -e '
    @S = stat STDIN 或 die "無效輸入; $!\n";
    S_IFREG($S[2]) 或死亡「輸入不是普通文件\n」;
    while (sysseek(STDIN,0,1) != $S[7]) { 睡眠 1 }
  '
  殺死$pid
  等待

答案4

你確實應該修復損壞的程序,但如果你不能,你可以通過讓 shell cat 文件並將其通過管道傳輸到程序來解決它,然後在退出之前休眠一兩秒,從而關閉管道並殺死使用SIGPIPE 的程式.

(cat file ; sleep 2) | stupid_program

相關內容