![Prozess beenden Endlosschleife Eingabeumleitung](https://rvso.com/image/31660/Prozess%20beenden%20Endlosschleife%20Eingabeumleitung.png)
Bearbeiten: Mir ist aufgefallen, dass das Programm, das ich ausführen wollte, nach dem Scannen aller meiner Eingaben eine Endlosschleife hatte. Dann wird einfach unendlich viel gedruckt, sodass das EOF nie gelesen wird. Es liest die letzte Eingabe und geht dann in eine Endlosschleife
Nehmen wir zum Beispiel an, das Programm sieht ungefähr so aus.
printf("%c\n", getch());
while(1)
{
printf("%c\n", getch());
}
Gibt es eine Möglichkeit, das Programm zu beenden, nachdem alle meine Eingaben eingelesen wurden? Ich möchte das Programm beenden können, wenn es mit der Umleitung der Eingabedatei fertig ist. (Wenn das Programm das letzte Zeichen aus der Eingabedatei erhält.) Hinweis: Ich kann das von mir ausgeführte Programm nicht ändern, aber ich kann das Skript ändern, das dieses Programm ausführt.
Im Moment habe ich dies in meinem Skript, um das Programm auszuführen. Im Grunde hilft es mir, Eingabe und Ausgabe zu kombinieren/zusammenzuführen.
BEARBEITEN: Jemand hat mir bei der Lösung meines früheren Problems geholfen. Ich sollte diesem Strace || break hinzufügen, aber ich habe ein neues Problem, das sich in der Bearbeitung oben befindet.
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
Ich habe also gerade eine wirklich ausgefallene Methode, um das Programm zu beenden, indem ich den Ruhezustand verwende und das Programm beende. Außerdem überprüfe ich die bisher verwendete Eingabe und vergleiche sie mit der Eingabedatei. Dies ist nur ein kleines Beispiel für das, was ich mache. Mein aktuelles Skript hat mehrere Timer und if-Vergleiche, die dem Code unten ähneln. Diese Methode ist jedoch nicht gut, denn wenn das Programm nach der von mir angegebenen Sekunde nicht mit dem Lesen der Eingabedatei fertig ist, wird das Programm nicht beendet. In meinem echten Skript habe ich mehrere dieser if-Anweisungen verwendet und es funktioniert in etwa 80 % der Fälle, aber manchmal macht es nicht das Richtige, wahrscheinlich wegen des Timers und der Schwankungen im Programmablauf.
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
Ich frage mich also, ob es eine Möglichkeit gibt, kill
den Vorgang strace
nach der Eingabe fortzusetzen. Außerdem möchte ich die Möglichkeit haben, Eingabe und Ausgabe zusammenzuführen.
Antwort1
Ich glaube, ich habe eine viel einfachere Lösung, bin mir aber nicht sicher, wie portabel sie ist. Bedenken Sie:
$ cat trunc.c
#include <stdio.h>
int main() {
int c;
while(((c = getchar()) != EOF) && ((c & 0xff) != 0xff))
putchar(c);
return 0;
}
Dies nutzt die Tatsache aus, dass auf einer Zweierkomplementmaschine (-1 & 0xff) == 0xff
. Dadurch wird Ihr Programm abgefangen, das EOF als Zeichen druckt. Ich habe es getestet:
your_buggy_source_gen < any_ascii_file | trunc
Dies entspricht „ cat any_ascii_file
vorausgesetzt, es enthält keine Oktette mit dem Wert 0xff“.
Antwort2
„Jetzt frage ich mich, ob es eine Möglichkeit gibt, zu prüfen, ob das Programm in eine Endlosschleife gerät, bevor alle Eingaben gelesen wurden. Wenn ein Programm beispielsweise in eine Endlosschleife gerät, ist dies die einzige Möglichkeit, das Programm zu stoppen.“
Wir kennen die Antwort aufdiese Frage seit fast achtzig JahrenDie Antwort ist nein, es gibt keine Möglichkeit.
Vielen Dank für die Frage, die nicht einmal annähernd Ihrer eigentlichen Frage entsprach:
„Ich versuche, die Leistungen der Studenten zu benoten. Ich möchte einfach ein Skript erstellen, das ich in Zukunft verwenden kann. Im Moment funktioniert es mit einem Sleep-Timer ganz gut, aber ich möchte das Skript ohne Timer robuster machen.“
Einfache Lösung:
# 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
Antwort3
Dies ist zwar nicht unbedingt ein Tipp zur Shell-Programmierung, aber tatsächlich müssen Sie das Verhalten des fehlerhaften Programms ändern, auch wenn Sie dessen Quellcode nicht ändern können.
Eine Möglichkeit hierfür wäre ein LD_PRELOAD-Modul, das den Systemaufruf „read()“ abfängt, indem es den eigentlichen Systemaufruf „read()“ ausführt und dann beendet, wenn eine Dateiende-Bedingung auftritt. (Idealerweise würde ich „getchar()“ abfangen, aber das ist normalerweise ein Makro und kein Funktionsaufruf und kann daher nicht abgefangen werden.)
Eine andere Möglichkeit besteht darin, den Code unter einem Debugger auszuführen, herauszufinden, wo er in die Endlosschleife gerät, und dann die Binärdatei zu patchen, entweder durch Ändern der ausführbaren Datei oder erneut durch Anwenden eines LD_PRELOAD-Moduls, das die fehlerhafte Funktion ersetzt.
Oder Sie könnten es so machen, wie in der vorherigen Antwort vorgeschlagen, und die Ausgabe in etwas umleiten, das seine Eingabe schließt, wenn das fehlerhafte Programm etwas ausgibt, das anzeigt, dass es das Ende seiner Eingabe erreicht hat. (Beachten Sie, dass dies darauf beruht,nichtSIGPIPE abfangen, was in diesem Fall eine faire Annahme ist.)
Eine ausgefeiltere Version davon wäre, den Eingabedateideskriptor mit einem anderen Programm zu teilen, das ihn nicht liest, sondern überwacht, ob er sein Ende erreicht hat. Dies ist jedoch eine besonders heikle Angelegenheit, insbesondere wenn Sie mit Pipes, Sockets oder Geräten arbeiten.
Angenommen, Ihr fehlerhaftes Programm liest von der Standardeingabe, umschließen Sie es mit etwa Folgendem:
#!/bin/sh Führen Sie ein fehlerhaftes Programm "$@" aus und pid=$! perl -MFcntl=:Modus -e ' @S = stat STDIN oder die "Ungültige Eingabe; $!\n"; S_IFREG($S[2]) oder die "Eingabe ist keine einfache Datei\n"; während (sysseek(STDIN,0,1) != $S[7]) { sleep 1 } ' $pid töten Warten
Antwort4
Sie sollten das defekte Programm wirklich reparieren, aber wenn das nicht geht, können Sie es umgehen, indem Sie die Shell die Datei caten und an das Programm weiterleiten lassen, dann für ein oder zwei Sekunden schlafen lassen, bevor sie beendet wird, wodurch die Pipe geschlossen und das Programm mit SIGPIPE beendet wird.
(cat file ; sleep 2) | stupid_program