C

write함수로 stdout(1)이 아닌 fd 값으로 출력 했을 때의 pipe와 redirection

또두 2023. 4. 11. 15:32

왜 이러한 글을 작성하게 되었는가?

42seoul 과제를 하던 중 오픈소스인 테스터기를 사용해 결과를 출력해보던 중 어떻게 봐도 맞는 답인데 틀리게 나와서 테스터기 코드를 분석하다가 알게 된 내용인데 더 자세히 남기고 싶어 작성한다.

 

테스터기에선 내 출력 결과를 파이프를 통해 grep에 넣어 맞는 결과를 선택해 비교하여 결과를 출력해주는 방식이었다.

해당 테스터기 코드도 문제가 없어 뭐가 다를까 생각했는데 내 코드에서 시스템 호출 시의 에러를 출력하는 부분이 일반적인 방법인 write함수에 stdout의 fd 값인 1을 넣어 출력하는 방식이 아니었다.

내 방식은 시스템 호출이 실패함에 따라 자동 설정된 errno에 따라 에러를 출력해주는 perror를 이용해 출력하는 방식이었다.

이 경우 fd가 stderr인 2로 출력이 된다.

이것이 틀렸다고 나오는 원인이었다.

내 방식은 stderr이므로 stdout의 결과를 받는 파이프(|)가 해당 결과를 받지 못해 grep으로 결과가 넘어가지 않아 발생한 문제였다.

왜 이렇게 된 것인지 궁금하여 알아보며 여러 테스트도 진행해봤다.

 

write함수의 첫번째 인자에 들어가는 fd 값은 일반적으로 stdout인 1로 넣어 호출한다.

그런데 그 외의 값을 넣게 되면 어떻게 되는지 테스트 해봤다.

 

#include <unistd.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	int val;

	if (argc != 2)
		return 0;

	val = atoi(argv[1]);
	if (val == 3)
	{
		write(1, "print stdout\n", 13);
		write(2, "print strerr\n", 13);
	}
	else
		write(val, "print something\n", 16);

	return 0;
}

 

위 코드를 컴파일 및 빌드해서 실행해서 아래와 같이 입력해보면 같은 결과가 출력된다.

./a.out 0	// print something

./a.out 1	// print something

./a.out 2	// print something

./a.out 3	// print stdout
		// print stderr

stdin(0), stdout(1), stderr(2) 모두 잘 출력되며 예외로 넣어둔 3의 경우 stdout과 stderr인 문장을 잘 출력하는 것을 볼 수 있다.

그럼 이번엔 아래의 명령들을 실행해보자

./a.out 0 | wc -l	// 0

./a.out 1 | wc -l	// 1

./a.out 2 | wc -l	// 0

./a.out 3 | wc -l	// 1

wc는 입력된 결과의 줄, 단어, 문자 수를 세주는 명령이고 -l 옵션으로 줄 수만 출력할 수 있다.

파이프로 해당 결과를 출력하면 위와 같이 나오는 데 1이 나온 것의 공통점은 fd가 stdout(1)인 출력 결과라는 점이다.

파이프는 왼쪽 프로그램(프로세스)의 stdout으로 출력되는 결과를 받아 오른쪽 프로그램(프로세스)에 stdin으로 식으로 프로세스끼리 통신을 할 수 있게 해주는 방식이다.

 

리다이렉션도 해봤다.

./a.out 0 > 0.txt	// ""
./a.out 1 > 1.txt	// "print something"
./a.out 2 > 2.txt	// ""
./a.out 3 > 3.txt	// "print stdout"

해당 파일에 저장된 내용은 위와 같다.

stdout인 것만 저장된 것을 볼 수 있다.

얘는 특이하게도 아래처럼 사용하면 다른 fd인 것도 처리가 가능하다.

./a.out 0 0> 0.txt	// "print something"
./a.out 1 1> 1.txt	// "print something"
./a.out 2 2> 2.txt	// "print something"
./a.out 3 2> 3.txt	// "print stderr"

fd가 stdout이 아니어도 모두 정상 처리 되었다.

위를 통해 output redirection엔 다른 fd의 결과도 처리할 수 있다는 것을 알 수 있었다.

 

fd가 4나 5를 넣어서 해봐도 가능하다.

단지 출력만 안될 뿐이다.