1. 정의 / 결론

리눅스에서 프로그램은 기본적으로 세 가지 입출력 흐름을 가진다.
stdin(0)은 입력, stdout(1)은 정상 출력, stderr(2)는 에러 출력이다.
리다이렉션은 이 출력 흐름을 파일이나 다른 대상으로 바꾸는 기능이다.

2. 핵심 요약

stdout은 정상 결과를 위한 출력 채널이고, stderr는 에러 메시지를 위한 출력 채널이다.
이 둘이 분리되어 있기 때문에 결과와 오류를 독립적으로 처리할 수 있다.
리다이렉션은 이 흐름을 파일, 다른 프로세스, 또는 버림 장치로 보내는 방법이다.

3. 왜 필요한가

프로그램의 출력이 하나로만 존재하면 정상 결과와 에러 메시지가 섞인다.
이 상태에서는 결과를 파일로 저장할 때 오류 메시지가 함께 들어가고, 후속 처리에서 파싱 오류가 발생할 가능성이 높다.

예를 들어 어떤 명령어 결과를 파일로 저장한다고 가정한다.
이때 오류 메시지가 함께 저장되면, 그 파일을 입력으로 사용하는 다음 단계에서 예상하지 못한 실패가 발생한다.
이 문제는 출력이 하나의 채널에 섞여 있기 때문에 발생한다.

리눅스는 이 문제를 해결하기 위해 출력 흐름을 두 개로 분리했다.
stdout은 정상 데이터만 흐르고, stderr는 오류 메시지만 흐른다.
이 구조 덕분에 결과만 저장하거나, 오류만 따로 수집하거나, 둘을 합치는 선택이 가능해진다.

리다이렉션은 이 분리된 흐름을 원하는 대상으로 보내는 방법이다.
즉, 출력 구조가 먼저 존재하고, 리다이렉션은 그 위에서 동작하는 제어 도구다.

핵심은 출력의 “내용”이 아니라 “경로”를 제어하는 것이다.

4. 예제

예제 1: stdout을 파일로 저장

echo "hello" > output.txt

→ 결과: output.txt에 hello가 저장된다.
→ 원인: >는 stdout(1)을 파일로 보낸다.
→ 실무 의미: 명령 결과를 파일로 저장할 때 기본적으로 사용된다.

예제 2: stderr만 파일로 저장

ls not_exist 2> error.txt

→ 결과: 에러 메시지가 error.txt에 저장된다.
→ 원인: 2>는 stderr(2)만 파일로 보낸다.
→ 실무 의미: 오류 로그만 따로 수집할 때 사용된다.

예제 3: stdout과 stderr를 함께 저장

ls file1 not_exist > all.txt 2>&1

→ 결과: 정상 출력과 에러가 모두 all.txt에 저장된다.
→ 원인: stderr를 stdout이 향하는 동일한 대상으로 보낸다.
→ 실무 의미: 전체 실행 로그를 하나로 남길 때 사용된다.

예제 4: 출력 버리기

ls not_exist > /dev/null 2>&1

→ 결과: 아무 출력도 남지 않는다.
→ 원인: stdout과 stderr를 모두 버림 장치로 보낸다.
→ 실무 의미: 불필요한 로그를 완전히 제거할 때 사용된다.

5. 기능 나열 (구조 위에서 동작하는 도구)

이 출력 구조 위에서 다음과 같은 도구들이 동작한다.

  • > : stdout을 파일로 보낸다
  • 2> : stderr를 파일로 보낸다
  • >> : 기존 파일에 이어서 기록한다
  • 2>&1 : stderr를 stdout과 동일한 대상으로 보낸다
  • /dev/null : 출력을 버린다
  • | : stdout을 다음 프로세스의 stdin으로 전달한다

이들은 모두 “출력 흐름을 제어한다”는 동일한 목적을 가진다.

각 개념은 별도로 더 깊게 다룰 수 있다.

6. 실무 적용

1) 배치 작업 로그 분리

상황은 배치 스크립트 실행이다.
문제는 정상 로그와 에러 로그가 섞이면 장애 분석이 어려워진다는 점이다.
해결 방법은 stdout과 stderr를 각각 다른 파일로 분리하는 것이다.
효과는 오류 원인을 빠르게 식별할 수 있다는 점이다.

command > success.log 2> error.log

2) 전체 로그 통합 저장

상황은 실행 결과를 하나의 로그 파일로 남겨야 하는 경우다.
문제는 출력 채널이 분리되어 있어 두 파일로 나뉜다는 점이다.
해결 방법은 stderr를 stdout에 붙여 하나로 만드는 것이다.
효과는 실행 로그를 한 파일로 관리할 수 있다는 점이다.

command > app.log 2>&1

3) 불필요한 로그 제거

상황은 반복 실행되는 스크립트에서 로그가 과도하게 쌓이는 경우다.
문제는 디스크 사용량 증가와 로그 노이즈다.
해결 방법은 출력 전체를 버리는 것이다.
효과는 로그 관리 부담이 줄어든다.

command > /dev/null 2>&1

7. 흔한 실수

1) 순서 잘못 사용

command 2>&1 > file.txt

→ 결과: stderr는 화면에 출력되고 stdout만 파일에 저장된다.
→ 원인: 리다이렉션은 왼쪽부터 적용되기 때문이다.
→ 해결: stdout을 먼저 보내고 stderr를 붙인다.

command > file.txt 2>&1

2) stderr가 파일에 안 들어가는 경우

command > file.txt

→ 결과: 에러 메시지는 화면에 남는다.
→ 원인: stdout만 리다이렉션했기 때문이다.
→ 해결: stderr도 명시적으로 처리해야 한다.

3) >> 사용 오해

command >> file.txt

→ 결과: 기존 내용 뒤에 추가된다.
→ 원인: append 동작이다.
→ 해결: 덮어쓰려면 >를 사용해야 한다.

4) /dev/null 오용

command > /dev/null

→ 결과: stdout만 버려지고 에러는 남는다.
→ 원인: stderr는 별도 채널이다.
→ 해결: stderr도 함께 버린다.

command > /dev/null 2>&1

8. 관련 개념

  • 파일 디스크립터(fd): 0, 1, 2로 구분되는 입출력 채널
  • 파이프(|): 프로세스 간 데이터 전달
  • /dev/null: 데이터를 버리는 특수 파일
  • 모든 것은 파일이다: 입출력 대상이 파일로 추상화됨

9. 더 깊이 보기

리눅스에서 stdout과 stderr는 파일 디스크립터로 구현된다.
각 프로세스는 실행 시 기본적으로 0, 1, 2번 디스크립터를 가진다.

이 구조는 입출력을 파일로 추상화하는 설계에서 나온다.
터미널, 파일, 네트워크 소켓 모두 동일한 인터페이스로 다뤄진다.
이 때문에 출력 대상 변경이 매우 단순한 문법으로 가능해진다.

리다이렉션은 내부적으로 파일 디스크립터를 다른 대상으로 연결하는 작업이다.
즉, 새로운 기능이 아니라 기존 입출력 구조를 재배치하는 과정이다.
이 설계 덕분에 작은 도구들을 조합해 강력한 파이프라인을 구성할 수 있다.

10. 정리

리눅스는 출력 흐름을 stdout과 stderr로 분리한다.
리다이렉션은 이 흐름을 파일이나 다른 대상으로 보내는 방법이다.
2>&1은 stderr를 stdout과 동일한 대상으로 보내는 문법이다.
출력 구조를 이해하면 로그 처리와 파이프라인 설계가 단순해진다.