Ich möchte einen automatischen C-Programmprüfer schreiben.
Zum Beispiel habe ich ein Spielzeugprogramm „hello.c“:
#include <stdio.h>
int main()
{
int a, b;
while (scanf("%d %d", (&a)-1000000000000000, &b) != EOF)
{
printf("%d\n", a+b);
}
return 0;
}
Und hier ist meine Eingabedatei „1.in“:
1 2
4 5
10 10
2 2
3 2
7 4
und Ausgabedatei „1.out“:
3
9
20
4
5
11
Ich verwende „gcc hello.c -o hello.o“, um ein ausführbares Programm „hello.o“ zu kompilieren und zu generieren. Offensichtlich trifft das Programm auf „Segmentfehler“:(Run in my MAC OS X)
$ ./hello.o <1.in
Segmentation fault: 11
Aber ich möchte einen automatischen Checker mit Pipe und Diff erstellen:
./hello.o <1.in | diff - 1.out
Und die Ausgabe ist:
0a1,6
> 3
> 9
> 20
> 4
> 5
> 11
Keine Fehlermeldungsanzeige! Aber ich möchte sie im Terminal anzeigen (MAC OS X).
Ich versuche, stderr wie folgt auf stdout umzuleiten:
./hello.o <1.in 2>&1 | diff - 1.out
Aber keine Wirkung!
Ich versuche auch, stderr in eine Datei umzuleiten wie:
./hello.o <1.in 2>log
und die Info „Segmentierungsfehler:11“ wird im Terminal angezeigt, obwohl nichts in der Datei ist.
Die gleiche Situation passiert, wenn ich
verwende./hello.o <1.in &>log
Vielleicht ist die Fehlerinfo nicht in stderr.
Also, wie kann ich dieses Problem lösen? Vielen Dank!
Akzeptierte Antwort:
HINWEIS: Ich habe hello.o
ersetzt mit hello
, da die .o
Die Dateierweiterung würde in diesem Zusammenhang normalerweise eine Objektdatei bezeichnen und nicht das endgültige ausführbare Programm.
Laut Ihrem Beitrag möchten Sie den Befehl ausführen:
./hello <1.in 2>&1 | diff - 1.out
Und Sie wollen die Fehlermeldung vom Ausführen von ./hello <1.in
in der Ausgabe dieses Befehls erscheinen. Die Fehlermeldung kommt jedoch nicht von hello.o
Programm selbst, aber von der Shell. Das Beste, was ich mir vorstellen kann, um den gewünschten Effekt mit einer einzigen Zeile zu erreichen, ist, den Befehl in einer Subshell auszuführen und dann diese Ausgabe mit Ihrem diff
zu verwenden Befehl:
2>&1 bash -c './hello <1.in' | diff - 1.out
Dies gibt uns die folgende Ausgabe:
1c1,6
< bash: line 1: 58469 Segmentation fault: 11 ./hello < 1.in
---
> 3
> 9
> 20
> 4
> 5
> 11
Der einzige Unterschied besteht darin, dass Sie in diesem Fall einige zusätzliche Metadaten von der Shell erhalten (z. B. die Zeilennummer und die Befehlszeichenfolge). Wenn Sie die Fehlermeldung genau replizieren möchten, können Sie trap
verwenden um einen Haken einzufügen, der genau die richtige Zeichenkette ausgibt.
Ich konnte keine Möglichkeit finden, die Fehlermeldung programmgesteuert zu extrahieren, also ging ich zum Bash-Quellcode und suchte nach der Meldung „Segmentation fault“. Ich habe es in einer Datei namens siglist.c gefunden, zusammen mit einer Reihe anderer Signale und Fehlerbeschreibungen. Mit diesen Informationen habe ich das folgende Skript geschrieben:
#!/bin/bash
# trapdesc.sh
#
# Take an error code from the `trap` command and
# print out the corresponding error message.
#
# Example usage:
#
# (trap 'bash trapdesc.sh $?' EXIT; <COMMAND>)
#
# List of signal codes and corresponding error messages
#
# Taken from bash source (siglist.c):
#
# https://github.com/tpruzina/bash/blob/master/siglist.c
#
declare -a SIGNALS=(
"SIGHUP":"Hangup"
"SIGINT":"Interrupt"
"SIGQUIT":"Quit"
"SIGILL":"Illegal instruction"
"SIGTRAP":"BPT trace/trap"
"SIGABRT":"ABORT instruction"
"SIGEMT":"EMT instruction"
"SIGFPE":"Floating point exception"
"SIGKILL":"Killed"
"SIGBUS":"Bus error"
"SIGSEGV":"Segmentation fault"
"SIGSYS":"Bad system call"
"SIGPIPE":"Broken pipe"
"SIGALRM":"Alarm clock"
"SIGTERM":"Terminated"
"SIGURG":"Urgent IO condition"
"SIGSTOP":"Stopped (signal)"
"SIGTSTP":"Stopped"
"SIGCONT":"Continue"
"SIGCLD":"Child death or stop"
"SIGTTIN":"Stopped (tty input)"
"SIGIO":"I/O ready"
"SIGXCPU":"CPU limit"
"SIGXFSZ":"File limit"
"SIGVTALRM":"Alarm (virtual)"
"SIGPROF":"Alarm (profile)"
"SIGWINCH":"Window changed"
"SIGLOST":"Record lock"
"SIGUSR1":"User signal 1"
"SIGUSR2":"User signal 2"
"SIGMSG":"HFT input data pending"
"SIGPWR":"power failure imminent"
"SIGDANGER":"system crash imminent"
"SIGMIGRATE":"migrate process to another CPU"
"SIGPRE":"programming error"
"SIGGRANT":"HFT monitor mode granted"
"SIGRETRACT":"HFT monitor mode retracted"
"SIGSOUND":"HFT sound sequence has completed"
"SIGINFO":"Information request"
)
# Make sure we get an integer
if ! [[ "$1" =~ ^[0-9]+$ ]]; then
2>&1 echo "Not a signal identifier: $1"
exit 1
fi
# Convert the signal from the `trap` function value to the signal ID
sid="$(($1 - 128))"
# Make sure the signal ID is in the valid range
if [[ "${sid}" -lt 0 || "${sid}" -gt 40 ]]; then
2>&1 echo "Unrecognized signal: ${sid}"
exit 1
fi
# Get the array-index for the signal
index="$((sid-1))"
# Get the signal description
description="$(echo ${SIGNALS[index]} | cut -d: -f2)"
# Print the error description
echo "${description}: ${sid}"
Mit diesem Skript können wir nun den folgenden Befehl ausführen:
(trap 'bash trapdesc.sh $?' EXIT; ./hello <1.in)
Dies erzeugt dieselbe Zeichenfolge wie die Ausführung von ./hello <1.in
:
Segmentation fault: 11
Aber jetzt können Sie diesen String aus dem Standardfehler (stderr) erfassen und an diff
leiten wie du wolltest:
(2>&1 trap 'bash trapdesc.sh $?' EXIT; ./hello <1.in) | diff - 1.out
Dies erzeugt genau die Ausgabe, die Sie erhalten hätten, wenn die Fehlermeldung in die ursprünglich erwartete Standardausgabe geschrieben worden wäre:
1c1,6
< Segmentation fault: 11
---
> 3
> 9
> 20
> 4
> 5
> 11