표준 오류 출력을 bash 변수로 리디렉션

표준 오류 출력을 bash 변수로 리디렉션

다음은 다음과 같은 결과를 가져올 C 코드 조각입니다 segfault.

// segfault.c

#include <string.h>

int main()
{
    memset((char *)0x0, 1, 100);
    return 1;
}

컴파일:

gcc segfault.c -o segfault

Bash에서 실행하는 경우:

$ ./segfault
Segmentation fault (core dumped)

이제 bash 스크립트로 호출을 래핑합니다. 연속으로 세 번 시도했습니다. 변수 내부에서 오류 출력을 가져 ret와서 표시하고 싶습니다 .

#!/bin/bash

# segfault.sh

ret=`./segfault 2>&1`
echo "1) " $ret
ret=`eval ./segfault 2>&1`
echo "2) " $ret
ret=`eval ./segfault 2>&1 | cat`
echo "3) " $ret

Bash에서 스크립트를 실행하는 경우:

1) 
2) 
3)  ./segfault.sh: line 7: 28814 Segmentation fault (core dumped) ./segfault

분명히 세 번째 호출 형식만 작동합니다. 제 질문은 처음 두 양식이 오류 출력을 캡처하지 못하는 이유는 무엇입니까?

답변1

단순화된 bash 스크립트에서만 작동합니다(만 stderr).

$ cat seg.sh 
#!/bin/bash
echo "Segfault" 1>&2
$ test=`./seg.sh`; echo "x$test"
Segfault
x
$ test=`./seg.sh 2>&1`; echo "x$test"
xSegfault
$ test=`eval ./seg.sh 2>&1`; echo "x$test"
xSegfault

귀하의 경우 문제는 Segmentation fault (core dumped)프로그램이 귀하의 프로그램에 의해 작성되는 것이 아니라(커널에 의해 종료되기 때문에) 하위 프로세스의 종료에 대한 정보를 얻는 상위 프로세스에 의해 작성된다는 사실로 인해 발생합니다. cat이전 예제에서 이 효과를 다른 프로세스에 배치하고 파이핑하여 숨깁니다. 종료 코드와 다음을 사용해야 합니다 stderr.

$ ./segfault; echo $?
Segmentation fault (core dumped)
139

답변2

"Segmentation failure (core dumped)"라는 메시지는 충돌하는 프로그램이 아니라 bash에 의해 표시됩니다(이 메시지가 표시되었을 때 프로그램은 이미 종료되었습니다!). 리디렉션은 프로그램 자체에만 적용됩니다.

셸 자체에서 프로그램에 대한 메시지를 리디렉션하려면 셸 그룹 구성 내에서 프로그램을 실행하고 전체 그룹의 출력을 리디렉션합니다. 가장 기본적인 쉘 그룹화 구성은 그룹화만 수행하는 중괄호입니다.

ret=`{ ./segfault; } 2>&1`

이 양식은 명령의 전체 평가 ret=`eval ./segfault 2>&1`에 리디렉션을 적용하므로 eval원칙적으로는 작동해야 하며 실제로는 bash 4.3.30 및 이전 시스템에서도 작동합니다. 일어날 수 있는 일(ksh로 재현 가능)은 bash 버전이 서브쉘의 마지막 명령일 때 서브루틴을 포크하지 않도록 일부 최적화를 갖추고 있다는 것입니다. 명령을 실행하는 명목상 방법은 다음 ret=`eval ./segfault`과 같습니다.

  • 파이프라인을 생성합니다.
  • 포크는 쉘 하위 프로세스를 만드는 것을 의미합니다. 하위 프로세스(프로세스 1)에서:
    • 출력을 파이프로 리디렉션합니다.
    • eval내장을 실행합니다 .
    • 십자가. 하위 프로세스(프로세스 2)에서:
      • 구현하다file ./segfault즉, 현재 프로세스에서 실행 중인 셸 프로그램을 이 프로그램으로 바꿉니다 segfault.
    • (프로세스 1에서) 프로세스 2가 완료될 때까지 기다립니다.
    • 프로세스 1이 종료됩니다.
  • (원래 쉘 프로세스에서) 파이프에서 데이터를 읽고 데이터를 ret변수에 누적합니다.
  • 파이프가 닫히면 실행이 계속됩니다.

보시다시피 프로세스 1은 다른 프로세스를 생성한 다음 완료될 때까지 기다렸다가 즉시 종료됩니다. 프로세스 1이 자체적으로 재활용하는 것이 더 효율적입니다. 일부 셸(및 셸 버전)은 이러한 상황을 인식하고 대응하는 데 있어 다른 것보다 뛰어납니다.테일콜 최적화. 그러나 프로세스 2의 경우 ret=`{ ./segfault; } 2>&1`프로세스 2는 표준 오류를 파일 설명자 1로 리디렉션하지만 프로세스 1은 그렇지 않습니다. 시도 중인 셸 버전에서는 최적화 프로그램이 이 경우를 인식하지 못합니다(아마도 tail call을 수행하지만 리디렉션을 다르게 설정해야 합니다).

관련 정보