BioPlayground

🧬
목록으로

표준 입출력 — stdin/stdout 리다이렉션

Python의 표준 입력/출력/에러 스트림을 이해하고, 터미널에서 리다이렉션과 파이프를 활용하는 방법을 배웁니다.

중급
|
10
|
검증 완료 (2026-07)
표준 입출력stdinstdoutstderr리다이렉션CLI
진행률0/18 (0%)

표준 입출력 — stdin/stdout 리다이렉션

이 토픽을 마치면

stdin, stdout, stderr의 역할을 설명할 수 있고, 터미널에서 리다이렉션(>, <)과 파이프(|)를 사용해서 프로그램의 입출력을 연결할 수 있습니다.


세 개의 통로

프로그램이 실행되면 운영체제가 자동으로 세 개의 통로를 열어줍니다:

이름방향용도Python
stdin (표준 입력)외부 → 프로그램키보드 입력input(), sys.stdin
stdout (표준 출력)프로그램 → 외부정상 결과print(), sys.stdout
stderr (표준 에러)프로그램 → 외부에러/경고sys.stderr

기본적으로 stdin은 키보드, stdout과 stderr는 화면(터미널)에 연결됩니다.


print()는 stdout에 쓴다

python
# hello.py
print("안녕하세요") # stdout으로 출력
bash
$ python hello.py
안녕하세요

print()는 내부적으로 sys.stdout.write()를 호출합니다. 직접 사용할 수도 있습니다:

python
import sys
sys.stdout.write("stdout으로 출력\n")
sys.stderr.write("stderr로 출력\n")

두 줄 모두 화면에 나타나지만, 서로 다른 통로를 통합니다. 이 차이는 리다이렉션에서 드러납니다.


리다이렉션 — 통로를 파일로 돌리기

터미널에서 ><를 사용하면 입출력 방향을 바꿀 수 있습니다.

stdout을 파일로 (>)

bash
$ python hello.py > output.txt

화면에는 아무것도 안 나옵니다. print()의 결과가 output.txt 파일로 들어갑니다.

bash
$ cat output.txt
안녕하세요

>>는 기존 내용에 이어쓰기:

bash
$ python hello.py >> output.txt # 기존 내용 뒤에 추가

stdin을 파일에서 (<)

python
# count.py
import sys
lines = sys.stdin.readlines()
print(f"총 {len(lines)}줄")
bash
$ python count.py < data.txt
42

data.txt의 내용이 키보드 입력 대신 stdin으로 들어갑니다.

stderr만 분리

python
# process.py
import sys
print("처리 결과: 성공")
sys.stderr.write("경고: 일부 데이터 누락\n")
bash
$ python process.py > result.txt 2> error.txt

>는 stdout만, 2>는 stderr만 리다이렉션합니다. 결과와 에러를 별도 파일로 분리할 수 있습니다.


파이프 — 프로그램을 연결하기

| (파이프)는 앞 프로그램의 stdout을 뒷 프로그램의 stdin에 연결합니다:

bash
$ cat data.csv | python process.py | python report.py > final.txt
text
cat → (stdout) → | → (stdin) → process.py → (stdout) → | → (stdin) → report.py → final.txt

각 프로그램은 자기 앞에서 오는 데이터만 받아서 처리하고, 결과를 다음으로 넘깁니다. 작은 프로그램을 조합해서 복잡한 작업을 처리하는 Unix 철학의 핵심입니다.


Python에서 stdin 한 줄씩 읽기

python
# upper.py — 입력을 대문자로 변환
import sys
for line in sys.stdin:
print(line.strip().upper())
bash
$ echo "hello world" | python upper.py
HELLO WORLD
$ cat names.txt | python upper.py
ALICE
BOB
CHARLIE

sys.stdin은 반복 가능(iterable)합니다. for문으로 한 줄씩 읽으면 메모리를 효율적으로 사용할 수 있습니다.


input()과 stdin의 관계

python
name = input("이름: ") # 프롬프트 → stderr? 아니요, stdout

input()은 내부적으로:

  1. 프롬프트 문자열을 stdout에 출력
  2. stdin에서 한 줄을 읽어서 반환

그래서 파이프와 함께 쓸 때 주의가 필요합니다:

bash
$ echo "철수" | python -c "name = input(); print(f'안녕, {name}')"
안녕, 철수

파이프에서는 프롬프트가 의미 없으므로, CLI 도구를 만들 때는 input() 대신 sys.stdin을 직접 읽는 편이 낫습니다.


실무 활용 예시

python
# csv_filter.py — 특정 조건의 행만 출력
import sys
import csv
reader = csv.reader(sys.stdin)
header = next(reader)
print(",".join(header))
for row in reader:
if float(row[2]) > 100: # 3번째 컬럼이 100 초과
print(",".join(row))
bash
$ cat sales.csv | python csv_filter.py > filtered.csv

stdin/stdout을 사용하면 파일 이름을 하드코딩하지 않아도 됩니다. 어떤 파일이든 파이프로 연결할 수 있으므로 재사용성이 높아집니다.


파이프 체이닝 — UNIX 철학

bash
# 로그에서 ERROR만 찾아 빈도순으로 정렬
$ cat server.log | grep "ERROR" | sort | uniq -c | sort -rn | head -5

각 프로그램이 하나의 일만 잘 하고, 파이프로 연결합니다. Python 스크립트도 이 체인의 한 고리가 될 수 있습니다:

python
# word_count.py — 단어 빈도 계산
import sys
from collections import Counter
words = []
for line in sys.stdin:
words.extend(line.strip().split())
for word, count in Counter(words).most_common(10):
print(f"{count:>5} {word}")
bash
$ cat article.txt | python word_count.py
42 the
31 and
28 to

stderr로 진행 상황 출력

python
import sys
total = 1000
for i in range(total):
# 처리 로직...
if i % 100 == 0:
print(f"진행: {i}/{total}", file=sys.stderr)
print(f"결과: {i * 2}") # 실제 출력 → stdout
bash
$ python process.py > results.txt
진행: 0/1000 ← 화면에 보임 (stderr)
진행: 100/1000
...

stdout은 파일로 리다이렉트되어 results.txt에 저장되고, stderr는 여전히 화면에 표시됩니다. 진행 상황 메시지와 결과 데이터를 분리하는 실용적인 패턴입니다.


핵심 정리

문법의미
>stdout을 파일로 (덮어쓰기)
>>stdout을 파일로 (이어쓰기)
<파일을 stdin으로
2>stderr를 파일로
|stdout → 다음 프로그램의 stdin
2>&1stderr를 stdout에 합침

/dev/null — 출력 버리기

bash
# stdout 버리기 (에러만 보기)
$ python noisy_script.py > /dev/null
# stderr 버리기 (결과만 보기)
$ python noisy_script.py 2> /dev/null
# 둘 다 버리기 (완전 무음)
$ python noisy_script.py > /dev/null 2>&1

/dev/null은 "쓰레기통"입니다. 자동화 스크립트에서 불필요한 출력을 억제할 때 사용합니다.

python
# Python에서도 동일한 패턴
import os, sys
if os.environ.get("QUIET"):
sys.stdout = open(os.devnull, "w")


subprocess — Python에서 파이프 구성

python
import subprocess
# 외부 명령 실행 + stdout 캡처
result = subprocess.run(
["ls", "-la"],
capture_output=True,
text=True
)
print(result.stdout)
print(result.stderr)
# 파이프 체이닝
p1 = subprocess.Popen(["cat", "data.txt"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "ERROR"], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0].decode()

Python 내에서 셸 명령의 stdin/stdout을 프로그래밍 방식으로 연결할 수 있습니다.


print()는 stdout, 에러 메시지는 stderr. 이 분리를 이해하면 리다이렉션과 파이프가 자연스러워집니다.