본문 바로가기
TYLER SONGBlog
← Blog index
CS학습5 min read

Redirect: 파일 디스크립터를 바꿔 입출력 방향을 제어하는 셸 기능

리다이렉션(Redirection)은 프로세스의 표준 입력·출력·에러가 향하는 대상을 키보드·화면에서 파일이나 다른 디스크립터로 바꾸는 셸 기능이다. 셸이 명령 실행 전에 파일 디스크립터(File Descriptor)를 `open`, `dup2` 시스템 콜로 재배치하는 방식으로 동작한다. `>`, `>>`, `<`, `2>`, `2>&1`의 의미와 순서 규칙

By 송민성

1. 개념

리다이렉션(Redirection)은 명령이 읽고 쓰는 표준 스트림(Standard Stream)의 연결 대상을 실행 전에 바꾸는 셸의 기능이다. 유닉스 계열 프로세스는 기본적으로 세 개의 스트림을 갖는다.

번호이름기본 연결 대상
0표준 입력(stdin)키보드(터미널)
1표준 출력(stdout)화면(터미널)
2표준 에러(stderr)화면(터미널)

이 번호가 파일 디스크립터(File Descriptor, FD)다. 리다이렉션은 이 번호가 가리키는 대상을 파일, 장치, 다른 FD로 교체한다.

주요 연산자는 다음과 같다.

문법의미
cmd > filestdout을 file로 보냄 (파일을 비우고 덮어씀)
cmd >> filestdout을 file 끝에 추가(append)
cmd < filefile을 stdin으로 사용
cmd 2> filestderr를 file로 보냄
cmd > file 2>&1stdout과 stderr를 모두 file로 보냄
cmd &> file위와 같은 효과의 bash 축약형
cmd << EOF히어 도큐먼트(Here Document)

2. 왜 사용하는가

  • 결과 저장: 명령 출력을 파일로 남겨 나중에 확인하거나 다른 도구에 넘긴다.
  • 출력 분리: 정상 결과와 에러 메시지를 따로 저장해 문제를 추적하기 쉽게 한다.
  • 자동화: 사용자 입력 대신 파일이나 히어 도큐먼트로 입력을 공급한다.
  • 소음 제거: 필요 없는 출력을 /dev/null로 버린다.

프로그램 자체는 "FD 1에 쓴다"만 알고 있고, 그 FD가 어디로 연결되는지는 모른다. 그래서 프로그램을 수정하지 않고도 입출력 대상을 바꿀 수 있다.

3. 동작 원리

리다이렉션은 프로그램이 아니라 셸이 처리한다. 셸은 자식 프로세스를 fork한 뒤, exec로 명령을 실행하기 직전에 FD를 재배치한다.

ls > out.txt의 흐름은 다음과 같다.

  1. 셸이 fork로 자식 프로세스를 만든다.
  2. 자식에서 open("out.txt", O_WRONLY | O_CREAT | O_TRUNC)로 파일을 연다. 새 FD(예: 3)를 얻는다.
  3. dup2(3, 1)로 FD 1이 out.txt를 가리키게 한다.
  4. 불필요한 FD 3을 close한다.
  5. exec로 ls를 실행한다. ls는 평소처럼 FD 1에 쓰지만 실제로는 파일에 기록된다.

exec는 열린 FD를 유지하므로 재배치된 상태가 새 프로그램에 그대로 넘어간다.

순서가 중요한 이유

리다이렉션은 왼쪽에서 오른쪽으로 차례대로 처리된다. 2>&1은 "FD 2를 *현재* FD 1이 가리키는 곳으로 복제"라는 뜻이다.

  • cmd > file 2>&1: 먼저 FD 1이 file을 가리키고, 그다음 FD 2가 FD 1의 대상(file)을 가리킨다. 둘 다 file로 간다.
  • cmd 2>&1 > file: 먼저 FD 2가 FD 1의 현재 대상(터미널)을 가리키고, 그다음 FD 1만 file로 바뀐다. stderr는 여전히 터미널로 나온다.

4. 코드 예제

shell
#!/usr/bin/env bash # 실습용 임시 디렉터리에서 실행한다. set -u workdir=$(mktemp -d) cd "$workdir" # 1) 덮어쓰기와 추가 echo "first" > log.txt echo "second" >> log.txt echo "--- log.txt" cat log.txt # 2) stdout과 stderr 분리 # 존재하는 파일(log.txt)과 존재하지 않는 파일(nofile)을 함께 조회 ls log.txt nofile > out.txt 2> err.txt echo "--- out.txt"; cat out.txt echo "--- err.txt"; cat err.txt # 3) 둘을 한 파일로 합치기 ls log.txt nofile > all.txt 2>&1 echo "--- all.txt"; cat all.txt # 4) 순서에 따른 차이: stderr가 터미널에 그대로 출력된다 echo "--- 순서가 반대인 경우(에러가 화면에 보임)" ls log.txt nofile 2>&1 > only_out.txt echo "--- only_out.txt"; cat only_out.txt # 5) 입력 리다이렉션 wc -l < log.txt # 6) 히어 도큐먼트 cat << 'EOF' 여러 줄 입력을 스크립트 안에서 바로 전달한다. EOF # 7) 출력 버리기 ls nofile > /dev/null 2>&1 echo "exit code: $?" # 8) 파이프와 함께 stderr 전달 (bash) ls log.txt nofile 2>&1 | grep -c "No such file" cd - > /dev/null rm -rf "$workdir"

5. 시간 복잡도 또는 성능 특성

리다이렉션 자체의 연산 비용은 실행당 open, dup2, close 같은 시스템 콜 몇 번 수준이며, 명령 실행 전체 비용에 비하면 작다. 성능에 영향을 주는 것은 주로 대상의 특성이다.

  • 파일 대상: 쓰기는 커널 페이지 캐시(page cache)를 거치므로 보통 디스크 속도에 즉시 묶이지 않는다. 데이터가 디스크에 반영되는 시점은 커널이 결정하며, 확실히 반영하려면 sync 계열 호출이 필요하다.
  • `/dev/null`: 쓰기를 모두 버리는 장치라 출력이 많은 명령의 출력 비용을 줄이는 데 쓸 수 있다.
  • 터미널 대상: 화면 렌더링이 병목이 될 수 있어, 대량 출력은 파일로 돌리는 편이 대체로 빠르다.

6. 실무 사용 사례

  • 크론(cron) 작업 로그: 0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
  • 배포·빌드 스크립트: 정상 로그와 에러 로그를 분리해 CI 아티팩트로 보관한다.
  • 백그라운드 실행: nohup ./server > server.log 2>&1 &
  • 설정 파일 생성: 히어 도큐먼트로 설정 파일을 만든다.
  • 권한이 필요한 파일에 쓰기: sudo tee와 파이프를 조합한다. sudo echo x > /etc/file은 리다이렉션을 sudo가 아닌 현재 사용자의 셸이 처리하므로 권한 오류가 날 수 있다. 대신 echo x | sudo tee /etc/file > /dev/null을 쓴다.

7. 주의할 점

  • `>`는 기존 내용을 즉시 지운다. 명령이 실패해도 파일은 이미 비워진 상태다. 같은 파일을 읽으면서 쓰는 sort data.txt > data.txt는 데이터를 잃는다. 임시 파일에 쓴 뒤 mv하거나 sort -o처럼 도구가 제공하는 옵션을 쓴다.
  • `2>&1` 순서: 반드시 > file 뒤에 둔다.
  • `&>`는 POSIX 표준이 아니다. bash 등 일부 셸의 확장이므로 이식성이 필요한 sh 스크립트에서는 > file 2>&1을 쓴다.
  • 덮어쓰기 방지: bash에서 set -o noclobber를 켜면 >가 기존 파일을 덮어쓰지 못한다. 강제로 덮어쓰려면 >|를 쓴다.
  • `sudo`와 리다이렉션: 위에서 설명한 대로 리다이렉션은 sudo 바깥의 셸이 수행한다.
  • 버퍼링: 파일이나 파이프로 연결되면 많은 프로그램이 stdout을 터미널일 때와 달리 완전 버퍼링(full buffering)으로 바꾼다. 그래서 로그가 늦게 보일 수 있다.

8. 핵심 정리

  • 리다이렉션은 프로세스의 FD 0(stdin), 1(stdout), 2(stderr)가 가리키는 대상을 바꾸는 셸 기능이다.
  • 셸이 fork 후 exec 직전에 open과 dup2로 FD를 재배치하므로 프로그램은 변경 없이 동작한다.
  • >는 덮어쓰기, >>는 추가, <는 입력, 2>는 에러 출력 변경이다.
  • 처리 순서는 왼쪽에서 오른쪽이므로 cmd > file 2>&1이 올바른 형태다.
  • 같은 파일을 읽으면서 >로 쓰지 않고, 권한이 필요한 파일에는 sudo tee를 쓴다.