쉘에서 pipe |는 왼쪽 명령의 stdout을 오른쪽 명령의 stdin으로 연결합니다. 예를 들어 cat file | grep error는 file 내용을 grep의 입력으로 넘깁니다.
운영체제 수준에서는 pipe라는 kernel buffer를 만들고, 두 process의 file descriptor를 연결합니다. 왼쪽 process는 pipe에 쓰고 오른쪽 process는 pipe에서 읽습니다. 기본적으로 stderr는 pipe로 연결되지 않고 terminal로 나갑니다. 필요하면 2>&1 같은 redirection으로 함께 보낼 수 있습니다.
꼬리질문
조금 더 깊게 물어본다면
답변 뒤에 이어질 수 있는 질문들을 하나씩 열어볼 수 있어요.
쉘을 만든다면 pipe를 어떻게 구현하나요?
pipe로 file descriptor 쌍을 만들고 fork한 뒤, 자식 process에서 dup2로 stdout/stdin을 pipe에 연결한 다음 exec를 호출합니다.
Pipe buffer가 가득 차면 어떻게 되나요?
읽는 쪽이 데이터를 소비하지 않아 buffer가 가득 차면 쓰는 process는 blocking될 수 있습니다. non-blocking mode라면 에러를 받을 수 있습니다.
`|`와 redirection은 어떻게 다른가요?
|는 process와 process를 연결하고, redirection은 file descriptor를 file이나 다른 stream으로 바꿔 연결합니다.
stderr는 pipe에서 어떻게 처리되나요?
기본적으로 |는 stdout만 연결합니다. stderr까지 넘기려면 2>&1 또는 shell별 redirection 문법을 사용해야 합니다.
stdin, stdout, stderr는 각각 무엇인가요?
프로세스가 기본으로 가지는 세 가지 표준 스트림입니다. stdin(fd 0)은 입력, stdout(fd 1)은 정상 출력, stderr(fd 2)는 에러 출력입니다. pipe는 기본적으로 stdout만 다음 프로세스의 stdin에 연결합니다.
`2>&1`은 어떤 의미인가요?
fd 2(stderr)를 fd 1(stdout)이 현재 가리키는 곳으로 리다이렉트하라는 뜻입니다. command 2>&1 | next처럼 쓰면 stderr도 stdout과 함께 pipe로 넘길 수 있습니다. 순서가 중요해서 2>&1을 pipe 앞에 써야 합니다.
pipe를 여러 개 연결할 때 성능 특성은?
cmd1 | cmd2 | cmd3처럼 연결하면 각 명령이 병렬로 실행되고, kernel pipe buffer를 통해 데이터를 주고받습니다. 처리 속도는 가장 느린 단계에 의해 결정됩니다. 각 단계가 streaming으로 처리하므로 전체 데이터를 메모리에 올리지 않아도 됩니다.
xargs는 어떤 문제를 해결하나요?
pipe는 stdout을 다음 명령의 stdin으로 넘기지만, 일부 명령은 stdin이 아닌 인수(argument)로 입력을 받습니다. xargs는 stdin에서 읽은 내용을 다음 명령의 argument로 변환해줍니다. 예를 들어 find . -name "*.log" | xargs rm은 find 결과를 rm의 인수로 전달합니다.
부가 설명
Pipe는 작은 프로그램을 조합해 복잡한 작업을 만드는 Unix 철학의 핵심입니다.
쉘을 직접 만든다면 pipe system call, fork, dup2, exec 흐름을 이해해야 합니다.
한 줄 정리
Pipe는 한 command의 표준 출력을 다른 command의 표준 입력으로 연결하는 IPC 메커니즘입니다.