print(file=sys.stderr)の使い方|Pythonで標準エラー出力(stderr)に書く方法
Pythonで標準エラー出力(stderr)に書くには、import sysをしてからprint("メッセージ", file=sys.stderr)と書きます。sys.stderrは通常の出力(sys.stdout)とは別の出力先なので、結果とエラー・ログを分けて保存したり、パイプでつないだ次のコマンドに渡さずに済ませたりできます。
この記事では、file=sys.stderrの書き方、sys.stderr.write()との違い、「invalid syntax」になる原因、出力の順番が入れ替わる理由、文字列として捕まえる方法を実行結果つきで説明します。コードはPython 3.12(macOS)で実行し、仕様はPython公式ドキュメントのsys.stderr(2026年10月18日確認)で確かめています。
print(file=sys.stderr)の書き方
import sys
print("通常の結果")
print("エラーメッセージ", file=sys.stderr)
端末で実行すると、どちらも画面に表示されるので違いが見えません。シェルのリダイレクトで振り分けると、別々の出力先になっていることが分かります。
$ python t.py > out.txt 2> err.txt
$ cat out.txt
通常の結果
$ cat err.txt
エラーメッセージ
>は標準出力、2>は標準エラー出力の保存先です。file=はprint()のキーワード引数で、書き込み先のファイルオブジェクトを指定します。sysをimportし忘れるとNameError: name 'sys' is not defined. Did you forget to import 'sys'?(Python 3.12の表示)になるので、先頭のimport sysを忘れないでください。
sys.stderrとは
公式ドキュメントでは、sys.stdin・sys.stdout・sys.stderrは「インタプリタが標準入力・標準出力・標準エラーに使うファイルオブジェクト」とされています。print()や式の結果はstdoutに、インタプリタ自身のエラーメッセージはstderrに出ます。
| 出力先 | 何を出すか | シェルでの番号 |
|---|---|---|
sys.stdout |
処理結果(ファイルに保存したり、次のコマンドに渡したりする値) | 1(>) |
sys.stderr |
進捗・警告・エラー(人が読むもの) | 2(2>) |
迷ったら「その出力を次のコマンドやファイルに渡したいか」で決めます。渡したいならstdout、渡したくないならstderrです。
結果とログを分けると何がうれしいか
import sys
def log(msg):
print(f"[LOG] {msg}", file=sys.stderr)
log("開始")
print("data,1")
print("data,2")
log("完了")
$ python lg.py > result.csv
[LOG] 開始
[LOG] 完了
$ cat result.csv
data,1
data,2
ログは画面に出たまま、CSVにはデータだけが入りました。ログも普通のprint()で出していたら、CSVの中に[LOG] 開始が混ざって使えなくなります。
sys.stderr.write()との違い
sys.stderr.write()でも書き込めますが、print()とは動きが違います。
import sys
n = sys.stderr.write("警告\n")
print(n)
警告
3
| 項目 | print(..., file=sys.stderr) |
sys.stderr.write() |
|---|---|---|
| 改行 | 自動で付く | 付かない(\nを自分で書く) |
| 渡せる値 | 何でも(自動で文字列に変換) | 文字列だけ。数値を渡すとTypeError: write() argument must be str, not int |
| 戻り値 | None | 書き込んだ文字数(上の例では3) |
ふだんはprint(..., file=sys.stderr)のほうが書きやすく、間違いも起きにくいです。
「invalid syntax」になる原因(Python 2の書き方)
file=sys.stderrでSyntaxErrorが出るのは、ほとんどの場合Python 2で実行しているときです。Python 2の公式ドキュメントには、Python 2ではprintが文(ステートメント)として扱われるため、関数版のprint()を使うには先頭にfrom __future__ import print_functionが必要だと書かれています。
python --versionでバージョンを確かめ、Python 3で実行する- どうしてもPython 2で動かす必要があるなら、ファイル先頭に
from __future__ import print_functionを書く
逆に、Python 2向けの古い書き方print >>sys.stderr, "hello"をPython 3で実行すると、次のエラーになりました。メッセージの中で正しい書き方まで案内されています。
TypeError: unsupported operand type(s) for >>: 'builtin_function_or_method' and '_io.TextIOWrapper'. Did you mean "print(<message>, file=<output_stream>)"?
Pythonのエラー表示やsys.exit()もstderrに出る
例外のトレースバック(Traceback)はstderrに出ます。そのためpython script.py > out.txtと保存しても、エラーだけは画面に残ります。
sys.exit()に文字列を渡した場合も同じです。
import sys
sys.exit("エラー: 設定ファイルが見つかりません")
$ python e3.py > o.txt
エラー: 設定ファイルが見つかりません
$ echo $?
1
メッセージは画面に出て、o.txtは空のまま、終了コードは1でした。sys.exit()の詳しい使い方は「Pythonでプログラムを終了する方法|sys.exit()・exit()・quit()の違い」で説明しています。
stdoutとstderrをまとめて保存すると順番が入れ替わる
両方を1つのファイルにまとめるには2>&1を付けます。ところが、そのまま実行すると順番が入れ替わることがあります。
import sys
print("1: stdout")
print("2: stderr", file=sys.stderr)
print("3: stdout")
$ python order.py > all.txt 2>&1
$ cat all.txt
2: stderr
1: stdout
3: stdout
$ python -u order.py > all.txt 2>&1
$ cat all.txt
1: stdout
2: stderr
3: stdout
理由はバッファリング(ためてから書き出す仕組み)の違いです。公式ドキュメントによると、stdoutは端末につながっているときは行ごと、ファイルやパイプにつながっているときはまとめて書き出されます。一方stderrはどちらの場合も行ごとに書き出されます(Python 3.9で、端末以外でも行ごとに変更)。そのため、ファイルに保存するとstdoutの行が後回しになります。
-uオプションか環境変数PYTHONUNBUFFEREDを使うと、両方ともバッファなしになり、書いた順に並びました。1か所だけすぐ出したいならprint(..., flush=True)でも構いません。stdout側の詳しい動きは「Python sys.stdoutの使い方」をご覧ください。
stderrの出力を文字列として捕まえる
テストなどでstderrに出た内容を確かめたいときは、contextlib.redirect_stderr()で書き込み先を一時的に差し替えます。別のプログラムを実行してそのstderrを受け取るときは、subprocess.run()のcapture_output=Trueを使います。
import sys, io, contextlib, subprocess
buf = io.StringIO()
with contextlib.redirect_stderr(buf):
print("警告: 設定が古い", file=sys.stderr)
print("捕まえた:", repr(buf.getvalue()))
r = subprocess.run([sys.executable, "-c", "import sys; print('NG', file=sys.stderr)"], capture_output=True, text=True)
print("子プロセスのstderr:", repr(r.stderr), "returncode:", r.returncode)
捕まえた: '警告: 設定が古い\n'
子プロセスのstderr: 'NG\n' returncode: 0
stderrに書いただけでは終了コードは0のままです。失敗として扱いたいときは、sys.exit(1)などで終了コードも返してください。標準出力の捕まえ方は「contextlib.redirect_stdout()」で詳しく説明しています。
loggingとの使い分け
loggingモジュールの既定の出力先もstderrです。logging.basicConfig()で設定したハンドラーの出力先がsys.stderrであることを、handlers[0].stream is sys.stderrがTrueになることで確かめました。
| 状況 | おすすめ |
|---|---|
| 使い捨てのスクリプト、数行のエラー表示 | print(..., file=sys.stderr) |
| INFO・WARNING・ERRORなどのレベル分けがほしい | logging |
| 時刻や出力元を自動で付けたい、ファイルにも残したい | logging |
どちらを使っても「結果はstdout、ログはstderr」という分け方は同じです。
よくある質問
Q. print(file=sys.stderr)でNameErrorになります。
sysをimportしていないことが原因です。ファイルの先頭にimport sysを書いてください。
Q. file=sys.stderrでinvalid syntaxになるのはなぜですか?
Python 2で実行している可能性が高いです。Python 2ではprintが文として扱われるため、Python 3で実行するか、先頭にfrom __future__ import print_functionを書きます。
Q. stderrに出力するとプログラムはエラー終了になりますか?
なりません。stderrに書いても終了コードは0のままです。エラーとして終わらせたいときは、sys.exit(1)などで0以外の終了コードを返します。
Q. stdoutとstderrを同じファイルに保存すると順番がずれるのはなぜですか?
ファイルに出すとき、stdoutはまとめて書き出され、stderrは行ごとに書き出されるためです。python -uで実行するか、print(…, flush=True)を使うと書いた順にそろいます。


