실행 가능한 C 프로그램의 오류 메시지를 stdout으로 리디렉션하는 방법은 무엇입니까? (맥 OS X)

실행 가능한 C 프로그램의 오류 메시지를 stdout으로 리디렉션하는 방법은 무엇입니까? (맥 OS X)

자동 C 프로그램 검사기를 작성하고 싶습니다. 예를 들어, 장난감 "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;
}

이것은 내 입력 파일 "1.in"입니다.

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

출력 파일 "1.out":

3
9
20
4
5
11

"gcc hello.c -o hello.o"를 사용하여 실행 프로그램 "hello.o"를 컴파일하고 생성합니다. 분명히 프로그램에 "segfault"가 발생했습니다. (내 MAC OS X에서 실행 중)

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

하지만 파이프와 차이점을 사용하여 자동 검사기를 만들고 싶습니다.

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

출력은 다음과 같습니다

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

오류 메시지가 표시되지 않습니다! 하지만 터미널(MAC OS X)에 표시하고 싶습니다.

다음과 같이 stderr를 stdout으로 리디렉션하려고 시도했습니다.

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

하지만 효과는 없습니다!

또한 stderr를 다음과 같은 파일로 리디렉션하려고 시도했습니다.

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

그리고 "Segmentation failure: 11"이라는 메시지가 터미널에 표시되지만 파일에는 아무것도 없습니다.

내가 사용할 때도 마찬가지입니다.

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

어쩌면 오류 메시지가 stderr에 없을 수도 있습니다.

그렇다면 이 문제를 어떻게 해결해야 할까요? 감사합니다!

답변1

프로그램의 표준 오류 스트림을 표준 출력으로 리디렉션했지만 해당 메시지가 프로그램에서 발행되지 않았기 때문에 "세그먼트 오류" 메시지가 리디렉션되지 않습니다. 대신, 프로그램을 호출하는 쉘에 의해 발행됩니다.

여기서 해야 할 일은 귀하의 상황에 따라 다릅니다.실제목표는 다음과 같습니다.

표준 오류를 표준 출력으로 리디렉션하려는 경우 다른 명령과 마찬가지로 C 프로그램을 사용하여 이를 수행할 수 있습니다. 이와 같은 리디렉션 2>&1및 사용하는 다른 방법은 제대로 작동합니다. 귀하의 프로그램은 실제로 표준 오류에 아무것도 쓰지 않지만 C 프로그램을 작성하는 경우하다이렇게 하면 리디렉션되는 것을 볼 수 있습니다. fputs또는 함수를 사용하여 fprintf작성할 수 있습니다 stderr. 예를 들면 다음과 같습니다.

fprintf(stderr, "error: refusing to say hello world\n");

반면, SIGSEGV리디렉션 명령 만들기를 사용하여 하위 프로세스를 종료한 후 셸에서 작성한 "세그먼트 오류" 메시지를 표준 오류로 리디렉션하는 것이 목표인 경우. 다른 명령을 사용할 필요는 없습니다.자일스는 설명한다, 명령 중 하나를 포함하면 충분합니다 { ;}. 예를 들어 다음을 실행할 수 있습니다.

{ ./hello.o 1000; } 2>>outfile

이는 프로그램 실행의 모든 ​​표준 오류 출력(프로그램에서 생성된 출력(이 경우 없음) 및 셸에서 생성된 출력 포함)을 파일에 추가합니다 outfile.

(그러나 나는 당신이 정말로 의심 스럽습니다.하다프로그램이 실제로 생성할 수 있는 오류 출력을 리디렉션하고 싶기 때문에 중복된 닫기로 표시하는 대신 이 질문에 대답하는 것입니다. )


C 프로그램이 명백히 가짜 주소에 쓰도록 하세요.~인 것 같다의도적으로 분할 오류를 발생시키는 것은 신뢰할 수 있는 방법이지만 실제로는 그렇지 않습니다. 이는 C 컴파일러가 정의되지 않은 동작이 발생하지 않는다고 가정할 수 있고 일부 컴파일러는 고급 최적화 없이 컴파일할 때에도 이 가정을 활용하기 때문입니다. 분할 오류 하에서 프로그램의 동작을 테스트하려면 프로그램 SIGSEGV이 실행되는 동안 프로그램에 신호를 보내는 것이 좋습니다. raise이 기능을 사용하여 스스로 이 작업을 수행 할 수도 있고 kill또는 killall명령을 사용하여 수행할 수도 있습니다.

예를 들어, 위의 예를 테스트하기 위해 { sleep 1000; } >&out다른 터미널에서 실행했습니다 killall -SEGV sleep. (이 sleep명령은 백그라운드에서 사용될 수 있으므로 프로덕션 시스템에서는 정확한 프로세스를 따르고 싶지 않을 수도 있음을 경고해야 합니다. 적어도 다른 중요한 작업을 수행하는 사용자는 아니며 물론 루트 사용자가 되기를 원하지 않습니다.

마지막으로 실행 파일의 이름을 접미사로 지정하고 싶지 않을 것입니다 .o. 이러한 접미사는 일반적으로 실제로 실행 가능한 파일을 생성하기 위해 서로 연결되어야 하는 컴파일러 생성 개체 파일에 사용되기 때문입니다. Unix 계열 운영 체제에서 실행 가능한 바이너리는 일반적으로 확장자 없이 이름이 지정됩니다.

답변2

노트:이 컨텍스트의 파일 확장자는 일반적으로 최종 실행 프로그램이 아닌 개체 파일을 나타내기 hello.o때문에 교체했습니다 .hello.o

귀하의 게시물에 따라 다음 명령을 실행하고 싶습니다.

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

./hello <1.in그리고 명령 출력에 런타임 오류 메시지가 표시되기를 원합니다 . 그러나 오류 메시지는 hello.o프로그램 자체에서 나오는 것이 아니라 셸에서 나옵니다. 한 줄에서 원하는 효과를 근사화하기 위해 제가 생각할 수 있는 가장 가까운 방법은 하위 쉘에서 명령을 실행한 다음 diff명령과 함께 이 출력을 사용하는 것입니다.

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

이는 다음과 같은 결과를 제공합니다.

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

유일한 차이점은 이 경우 쉘 출력의 추가 메타데이터(예: 줄 번호 및 명령 문자열)를 얻을 수 있다는 것입니다. 오류 메시지를 정확하게 복제하려면 trap후크 삽입을 사용하여 올바른 문자열을 인쇄할 수 있습니다.

프로그래밍 방식으로 오류 메시지를 추출하는 방법을 찾을 수 없어서배쉬 소스 코드"분할 오류" 메시지를 검색합니다. 라는 파일에서 찾았습니다.siglist.c, 기타 여러 가지 신호 및 오류 설명이 포함되어 있습니다. 이 정보를 사용하여 다음 스크립트를 작성했습니다.

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

이제 이 스크립트를 사용하여 다음 명령을 실행할 수 있습니다.

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

그러면 다음을 실행하는 것과 동일한 문자열이 생성됩니다 ./hello <1.in.

Segmentation fault: 11

diff하지만 이제 표준 오류(stderr)에서 해당 문자열을 캡처하여 원하는 위치 로 파이프할 수 있습니다 .

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

이는 오류 메시지가 원래 예상했던 표준 출력에 기록된 경우 얻을 수 있는 정확한 출력을 생성합니다.

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

관련 정보