標準入出力 — stdin/stdout のリダイレクト
このトピックを終えると
stdin、stdout、stderr の役割を説明でき、ターミナルでリダイレクト (>, <) とパイプ (|) を使用してプログラムの入出力を接続できるようになります。
3つの経路
プログラムが実行されると、オペレーティングシステムは自動的に3つの経路を開きます。
| 名前 | 方向 | 用途 | Python |
|---|---|---|---|
| stdin (標準入力) | 外部 → プログラム | キーボード入力 | input()、sys.stdin |
| stdout (標準出力) | プログラム → 外部 | 通常の結果 | print()、sys.stdout |
| stderr (標準エラー) | プログラム → 外部 | エラー/警告 | sys.stderr |
通常、stdin はキーボードに、stdout と stderr は画面(ターミナル)に接続されます。
print() は stdout に書き込む
# hello.pyprint("こんにちは") # stdout に出力$ python hello.pyこんにちはprint() は内部的に sys.stdout.write() を呼び出します。直接使用することもできます。
import sys
sys.stdout.write("stdout に出力\n")sys.stderr.write("stderr に出力\n")どちらの行も画面に表示されますが、異なる経路を通ります。この違いはリダイレクトで明らかになります。
リダイレクト — 経路をファイルに転換する
ターミナルで > と < を使用すると、入出力の方向を変えることができます。
stdout をファイルに (>)
$ python hello.py > output.txt画面には何も表示されません。print() の結果が output.txt ファイルに書き込まれます。
$ cat output.txtこんにちは>> は既存の内容に追加します。
$ python hello.py >> output.txt # 既存の内容の後に追記stdin をファイルから (<)
# count.pyimport syslines = sys.stdin.readlines()print(f"合計 {len(lines)} 行")$ python count.py < data.txt合計 42 行data.txt の内容がキーボード入力の代わりに stdin に入力されます。
stderr のみを分離する
# process.pyimport sysprint("処理結果:成功")sys.stderr.write("警告:一部のデータが欠落\n")$ python process.py > result.txt 2> error.txt> は stdout のみを、2> は stderr のみをリダイレクトします。結果とエラーを別のファイルに分離できます。
パイプ — プログラムを接続する
| (パイプ) は、前のプログラムの stdout を、次のプログラムの stdin に接続します。
$ cat data.csv | python process.py | python report.py > final.txtcat → (stdout) → | → (stdin) → process.py → (stdout) → | → (stdin) → report.py → final.txt各プログラムは、自分の前のプログラムから送られてくるデータのみを受け取り、処理し、結果を次のプログラムに渡します。小さなプログラムを組み合わせて複雑な処理を行う、Unix 哲学の核心です。
Python で stdin を 1 行ずつ読み取る
# upper.py — 入力を大文字に変換import sys
for line in sys.stdin: print(line.strip().upper())$ echo "hello world" | python upper.pyHELLO WORLD
$ cat names.txt | python upper.pyALICEBOBCHARLIEsys.stdin はイテラブルです。for ループで 1 行ずつ読み込むと、メモリを効率的に使用できます。
input() と stdin の関係
name = input("名前: ") # プロンプト → stderr? いいえ、stdoutinput() は内部的に:
- プロンプト文字列を stdout に出力
- stdin から 1 行読み込んで返す
そのため、パイプと一緒に使用する際には注意が必要です。
$ echo "鉄男" | python -c "name = input(); print(f'こんにちは、{name}')"こんにちは、鉄男パイプではプロンプトは意味がないため、CLI ツールを作成する際には input() ではなく、sys.stdin を直接読み取る方が適切です。
実際の活用例
# csv_filter.py — 特定の条件の行のみ出力import sysimport 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))$ cat sales.csv | python csv_filter.py > filtered.csvstdin/stdout を使用すると、ファイル名をハードコーディングする必要がありません。どのファイルでもパイプで接続できるため、再利用性が高くなります。
パイプチェーン — UNIX 哲学
# ログから ERROR のみを検索し、頻度順にソート$ cat server.log | grep "ERROR" | sort | uniq -c | sort -rn | head -5各プログラムが 1 つのタスクをうまく行い、パイプで接続します。Python スクリプトもこのチェーンの 1 つの環になることができます。
# word_count.py — 単語の頻度を計算import sysfrom 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}")$ cat article.txt | python word_count.py 42 the 31 and 28 tostderr に進行状況を出力する
import sys
total = 1000for i in range(total): # 処理ロジック... if i % 100 == 0: print(f"進行: {i}/{total}", file=sys.stderr)
print(f"結果: {i * 2}") # 実際の出力 → stdout$ python process.py > results.txt進行: 0/1000 ← 画面に表示 (stderr)進行: 100/1000...stdout はファイルにリダイレクトされて results.txt に保存され、stderr は引き続き画面に表示されます。進行状況メッセージとデータを出力から分離する実用的なパターンです。
重要なまとめ
| 構文 | 意味 |
|---|---|
> | stdout をファイルに (上書き) |
>> | stdout をファイルに (追記) |
< | ファイルを stdin に |
2> | stderr をファイルに |
| ` | ` |
2>&1 | stderr を stdout に統合 |
/dev/null — 出力を破棄する
# 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 でも同じパターンimport os, sys
if os.environ.get("QUIET"): sys.stdout = open(os.devnull, "w")subprocess — 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。この分離を理解すると、リダイレクトとパイプが自然になります。