쉘 리다이렉션(2>&1)과 파일 디스크립터(FD) 이해하기
우리는 흔히 > file이나 2>&1 같은 구문을 사용합니다. 단순히 “저장한다”, “에러를 합친다”라고 외워서 쓰지만, 커널 내부에서 어떤 일이 일어나는지 이해하면 리눅스 시스템을 더 깊게 다룰 수 있습니다.
1. 파일 디스크립터(FD)란?
리눅스/유닉스 같은 POSIX 시스템에서 프로세스가 파일(또는 소켓, 파이프)에 접근할 때 사용하는 추상적인 식별 번호(Non-negative Integer) 입니다. 프로세스가 실행되면 커널은 기본적으로 3개의 FD를 할당합니다.
- 0 (stdin) : 표준 입력 (키보드)
- 1 (stdout) : 표준 출력 (모니터)
- 2 (stderr) : 표준 에러 (모니터)
2. 2>&1의 동작 원리: 포인터 복사
pnpm build | pbcopy를 실행하면, 쉘은 기본적으로 FD 1(stdout) 만 파이프로 연결합니다. FD 2(stderr) 는 여전히 모니터를 가리키고 있어서 에러 로그가 파이프를 타지 못하는 것입니다.
2>&1은 “FD 2가 가리키는 주소를 FD 1이 가리키는 주소와 똑같이 복제하라” 는 명령입니다. (시스템 콜 dup2(1, 2) 호출)
순서가 생명이다
FD는 포인터 복사 개념이므로 순서가 매우 중요합니다.
- ✅ 올바른 예:
> log.txt 2>&1
- FD 1이
log.txt를 가리킴. - FD 2가 FD 1을 따라
log.txt를 가리킴. (둘 다 파일로 저장 )
- ❌ 잘못된 예:
2>&1 > log.txt
- FD 2가 FD 1(현재 모니터)을 따라 모니터를 가리킴.
- FD 1이
log.txt로 변경됨. (에러는 화면에, 정상 출력만 파일로 )
3. 리눅스에서 파일을 생성하는 6가지 방법 (FD 관점)
파일을 만드는 것도 결국 커널에게 FD를 발급받는(open) 과정입니다. 목적에 따라 다양한 방법이 있습니다.
3.1. 리다이렉션 (>)
가장 기본적인 방법입니다. > empty.txt만 쳐도 파일이 생성됩니다. 커널은 O_CREAT | O_TRUNC 플래그를 사용하여 파일을 생성하거나 기존 파일을 비웁니다.
# 빈 파일 생성
> empty.txt
# 명령어 출력을 파일로 저장
echo "Hello" > output.txt
3.2. touch
파일이 없으면 0바이트 파일을 생성하고, 있으면 타임스탬프만 갱신합니다. 안전한 생성을 원할 때 사용합니다.
# 파일 생성 (없으면 생성, 있으면 타임스탬프만 갱신)
touch newfile.txt
3.3. cat <<EOF (Here-doc)
설정 파일 등 여러 줄의 텍스트를 한 번에 파일로 쓸 때 유용합니다. 리다이렉션 순서는 중요하지 않으므로 두 가지 방식 모두 가능합니다.
# 방식 1: 리다이렉션을 앞에
cat > config.txt <<EOF
Hello
World
EOF
# 방식 2: Here-doc을 앞에
cat <<EOF > config.txt
Hello
World
EOF
3.4. install
CI/CD 스크립트에서 많이 사용합니다. 파일 복사(cp)와 동시에 권한(chmod)을 부여하고 디렉토리(mkdir)까지 만들어줍니다.
# 파일 복사 및 권한 설정을 한 번에
install -m 755 script.sh /bin/script.sh
3.5. fallocate
테스트용 대용량 파일이 필요할 때 dd보다 훨씬 빠릅니다. 실제 디스크 I/O 없이 메타데이터만 조작하여 공간을 할당합니다.
# 1GB 파일을 0.1초 만에 생성
fallocate -l 1G fast_dummy.img
3.6. mktemp
쉘 스크립트에서 임시 파일이 필요할 때 사용합니다. 랜덤한 이름으로 생성해주어 파일명 충돌(Race Condition)을 방지합니다.
# 임시 파일 생성
TEMP_FILE=$(mktemp)
echo "임시 데이터" > "$TEMP_FILE"
# 사용 후 삭제
rm "$TEMP_FILE"
4. 시스템 장애 예방: Open Files Limit
서버 운영 시 “Too many open files” 에러를 종종 봅니다. 이는 메모리가 부족한 게 아니라, 프로세스가 사용할 수 있는 FD 번호를 다 썼기 때문 입니다.
리눅스는 프로세스당 열 수 있는 파일 개수(소켓 포함)를 제한합니다. (기본값은 보통 1024로 매우 작음)
- 확인:
ulimit -n - 해결: Nginx나 DB 같은 고성능 서버는 반드시 이 제한(
Soft/Hard Limit)을/etc/security/limits.conf에서 늘려줘야 동시 접속자를 처리할 수 있습니다.
결론
“리눅스에서 모든 것은 파일이다”라는 철학을 이해하면, pbcopy를 쓰기 위해 에러 스트림을 조작하거나, 서버 튜닝을 위해 FD 리밋을 조절하는 작업이 모두 연결된 개념임을 알 수 있습니다.