GNU/Linux >> Tutoriels Linux >  >> Linux

Comment rediriger les informations d'erreur du programme C exécutable vers Stdout ? (Mac OS X)?

Je veux écrire un vérificateur de programme automatique en C.
Par exemple, j'ai un petit programme "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;
}

Et voici mon fichier d'entrée "1.in":

1 2
4 5
10 10
2 2
3 2
7 4

et le fichier de sortie "1.out":

3
9
20
4
5
11

J'utilise "gcc hello.c -o hello.o" pour compiler et générer un programme exécutable "hello.o". Évidemment, le programme rencontrera "défaut de segment":(Exécuté sous mon MAC OS X)

$ ./hello.o <1.in
Segmentation fault: 11

Mais je veux faire un vérificateur automatique en utilisant pipe et diff :

./hello.o <1.in | diff - 1.out

Et le résultat est :

0a1,6
> 3
> 9
> 20
> 4
> 5
> 11

Aucun message d'erreur affiché ! Mais je veux les afficher dans le terminal (MAC OS X).

J'essaie de rediriger stderr vers stdout comme :

./hello.o <1.in 2>&1 | diff - 1.out

Mais aucun effet !

J'essaie également de rediriger stderr vers un fichier comme :

./hello.o <1.in 2>log

et l'info "Défaut de segmentation :11" s'affiche dans le terminal alors qu'il n'y a rien dans le fichier.

La même situation se produit lorsque j'utilise

./hello.o <1.in &>log

Peut-être que les informations d'erreur ne sont pas dans stderr.

Alors, comment puis-je résoudre ce problème? Merci !

Réponse acceptée :

REMARQUE : J'ai remplacé hello.o avec hello , puisque le .o l'extension de fichier dans ce contexte désignerait généralement un fichier objet et non le programme exécutable final.

D'après votre message, vous souhaitez lancer la commande :

./hello <1.in 2>&1 | diff - 1.out

Et vous voulez que le message d'erreur exécute ./hello <1.in à apparaître dans la sortie de cette commande. Cependant, le message d'erreur ne provient pas de hello.o programme lui-même, mais depuis le shell. La chose la plus proche à laquelle je puisse penser pour se rapprocher de l'effet souhaité avec une seule ligne est d'exécuter la commande dans un sous-shell, puis d'utiliser cette sortie avec votre diff commande :

2>&1 bash -c './hello <1.in' | diff - 1.out

Cela nous donne la sortie suivante :

1c1,6
< bash: line 1: 58469 Segmentation fault: 11  ./hello < 1.in
---
> 3
> 9
> 20
> 4
> 5
> 11

La seule différence est que dans ce cas, vous obtenez une sortie de métadonnées supplémentaires par le shell (c'est-à-dire le numéro de ligne et la chaîne de commande). Si vous souhaitez reproduire exactement le message d'erreur, vous pouvez utiliser trap pour insérer un crochet qui imprime exactement la bonne chaîne.

Je n'ai pas trouvé de moyen d'extraire par programme le message d'erreur. Je suis donc allé dans le code source de Bash et j'ai recherché le message "Défaut de segmentation". Je l'ai trouvé dans un fichier appelé siglist.c, avec un tas d'autres signaux et descriptions d'erreurs. En utilisant ces informations, j'ai écrit le script suivant :

#!/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}"

Maintenant, en utilisant ce script, nous pouvons exécuter la commande suivante :

(trap 'bash trapdesc.sh $?' EXIT; ./hello <1.in)

Cela produit la même chaîne que l'exécution de ./hello <1.in :

Segmentation fault: 11

Mais maintenant, vous pouvez capturer cette chaîne à partir de l'erreur standard (stderr) et la diriger vers diff comme tu voulais :

(2>&1 trap 'bash trapdesc.sh $?' EXIT; ./hello <1.in) | diff - 1.out

Cela produit la sortie exacte que vous auriez obtenue si le message d'erreur avait été écrit sur la sortie standard que vous attendiez à l'origine :

1c1,6
< Segmentation fault: 11
---
> 3
> 9
> 20
> 4
> 5
> 11

Linux
  1. Comment utiliser Nginx pour rediriger

  2. Comment envoyer Stdout à plusieurs commandes ?

  3. Comment rediriger la sortie d'un programme vers un fichier Zip ? ?

  4. Comment rediriger Stderr et Stdout vers différents fichiers et également les afficher dans le terminal ?

  5. Comment définir un fichier comme NON exécutable ?

Comment rediriger HTTP vers HTTPS dans Nginx

Comment vérifier la version Java sur Mac ou Windows

Comment dépanner ERR_TOO_MANY_REDIRECTS

Comment rediriger stderr vers stdout dans Bash

Comment rendre un fichier exécutable sous Linux

Comment changer l'adresse MAC sous Linux