Voglio scrivere un programma di controllo automatico in C.
Ad esempio, ho un programma giocattolo "ciao.c":
#include <stdio.h>
int main()
{
int a, b;
while (scanf("%d %d", (&a)-1000000000000000, &b) != EOF)
{
printf("%d\n", a+b);
}
return 0;
}
Ed ecco il mio file di input “1.in”:
1 2
4 5
10 10
2 2
3 2
7 4
e file di output “1.out”:
3
9
20
4
5
11
Uso "gcc hello.c -o hello.o" per compilare e generare un programma eseguibile "hello.o". Ovviamente, il programma incontrerà un "errore di segmento":(Esegui nel mio MAC OS X)
$ ./hello.o <1.in
Segmentation fault: 11
Ma voglio creare un controllo automatico usando pipe e diff:
./hello.o <1.in | diff - 1.out
E l'output è:
0a1,6
> 3
> 9
> 20
> 4
> 5
> 11
Nessun messaggio di errore visualizzato! Ma voglio visualizzarli nel terminale (MAC OS X).
Provo a reindirizzare stderr a stdout come:
./hello.o <1.in 2>&1 | diff - 1.out
Ma nessun effetto!
Provo anche a reindirizzare stderr a un file come:
./hello.o <1.in 2>log
e l'info "Errore di segmentazione:11" viene visualizzato nel terminale mentre nulla nel file.
La stessa situazione accade quando uso
./hello.o <1.in &>log
Forse le informazioni sull'errore non sono in stderr.
Quindi, come posso risolvere questo problema? Grazie!
Risposta accettata:
NOTA: Ho sostituito hello.o
con hello
, poiché il .o
l'estensione del file in questo contesto in genere denota un file oggetto e non il programma eseguibile finale.
Secondo il tuo post, vuoi eseguire il comando:
./hello <1.in 2>&1 | diff - 1.out
E vuoi che il messaggio di errore esegua ./hello <1.in
per apparire nell'output di questo comando. Tuttavia, il messaggio di errore non proviene da hello.o
programma stesso, ma dalla shell. La cosa più vicina a cui riesco a pensare per approssimare l'effetto desiderato con una singola riga è eseguire il comando in una subshell e quindi utilizzare questo output con il tuo diff
comando:
2>&1 bash -c './hello <1.in' | diff - 1.out
Questo ci dà il seguente output:
1c1,6
< bash: line 1: 58469 Segmentation fault: 11 ./hello < 1.in
---
> 3
> 9
> 20
> 4
> 5
> 11
L'unica differenza è che in questo caso si ottengono ulteriori metadati in uscita dalla shell (ovvero il numero di riga e la stringa di comando). Se vuoi replicare esattamente il messaggio di errore, puoi usare trap
per inserire un gancio che stampi esattamente la stringa giusta.
Non riuscivo a trovare un modo per estrarre a livello di codice il messaggio di errore, quindi sono andato al codice sorgente di Bash e ho cercato il messaggio "Errore di segmentazione". L'ho trovato in un file chiamato siglist.c, insieme a un sacco di altri segnali e descrizioni di errori. Usando queste informazioni ho scritto il seguente script:
#!/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}"
Ora, usando questo script, possiamo eseguire il seguente comando:
(trap 'bash trapdesc.sh $?' EXIT; ./hello <1.in)
Questo produce la stessa stringa dell'esecuzione di ./hello <1.in
:
Segmentation fault: 11
Ma ora puoi catturare quella stringa dall'errore standard (stderr) e reindirizzarla a diff
come volevi:
(2>&1 trap 'bash trapdesc.sh $?' EXIT; ./hello <1.in) | diff - 1.out
Questo produce l'output esatto che avresti ottenuto se il messaggio di errore fosse stato scritto nell'output standard che ti aspettavi inizialmente:
1c1,6
< Segmentation fault: 11
---
> 3
> 9
> 20
> 4
> 5
> 11