GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo redirigir la información de error del programa C ejecutable a la salida estándar? (Mac OS X)?

Quiero escribir un verificador automático de programas en C.
Por ejemplo, tengo un programa de juguete "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;
}

Y aquí está mi archivo de entrada "1.in":

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

y el archivo de salida “1.out”:

3
9
20
4
5
11

Uso "gcc hello.c -o hello.o" para compilar y generar un programa ejecutable "hello.o". Obviamente, el programa encontrará "falla de segmento":(Ejecutar en mi MAC OS X)

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

Pero quiero hacer un verificador automático usando tubería y diferencia:

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

Y la salida es:

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

¡No se muestra ningún mensaje de error! Pero quiero mostrarlos en la terminal (MAC OS X).

Intento redirigir stderr a stdout como:

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

¡Pero ningún efecto!

También trato de redirigir stderr a un archivo como:

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

y la información "Error de segmentación:11" se muestra en el terminal mientras no hay nada en el archivo.

La misma situación ocurre cuando uso

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

Tal vez la información del error no esté en stderr.

Entonces, ¿cómo puedo resolver este problema? ¡Gracias!

Respuesta aceptada:

NOTA: He reemplazado hello.o con hello , ya que el .o la extensión de archivo en este contexto normalmente indicaría un archivo de objeto y no el programa ejecutable final.

De acuerdo con su publicación, desea ejecutar el comando:

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

Y desea que el mensaje de error ejecute ./hello <1.in que aparezca en la salida de este comando. Sin embargo, el mensaje de error no proviene de hello.o programa en sí, pero desde el shell. Lo más parecido que se me ocurre para aproximar el efecto deseado con una sola línea es ejecutar el comando en una subcapa y luego usar esta salida con su diff comando:

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

Esto nos da el siguiente resultado:

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

La única diferencia es que, en este caso, el shell obtiene una salida de metadatos adicional (es decir, el número de línea y la cadena de comando). Si desea replicar exactamente el mensaje de error, puede usar trap para insertar un gancho que imprima exactamente la cadena correcta.

No pude encontrar una forma de extraer el mensaje de error mediante programación, así que fui al código fuente de Bash y busqué el mensaje "Error de segmentación". Lo encontré en un archivo llamado siglist.c, junto con un montón de otras señales y descripciones de errores. Usando esa información, escribí el siguiente 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}"

Ahora, usando este script, podemos ejecutar el siguiente comando:

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

Esto produce la misma cadena que ejecutar ./hello <1.in :

Segmentation fault: 11

Pero ahora puede capturar esa cadena del error estándar (stderr) y canalizarla a diff como querías:

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

Esto produce el resultado exacto que habría obtenido si el mensaje de error se hubiera escrito en el resultado estándar que esperaba originalmente:

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

Linux
  1. Cómo usar Nginx para redirigir

  2. Ssh – ¿Redirigir Stdout sobre Ssh?

  3. ¿Cómo hacer que un programa sea ejecutable desde cualquier lugar?

  4. ¿Cómo obtener la dirección MAC de su máquina usando un programa C?

  5. ¿Cómo sale del programa X11 sin error?

Cómo redirigir HTTP a HTTPS en Nginx

Cómo comprobar la versión de Java en Mac o Windows

Cómo solucionar problemas de ERR_TOO_MANY_REDIRECTS

Cómo redirigir stderr a stdout en Bash

Cómo hacer un archivo ejecutable en Linux

Cómo cambiar la dirección MAC en Linux