Programs that use the standard streams correctly can be combined with pipes and redirection like any Unix tool.
Every process starts with three open standard streams, which sys exposes as text file objects:
sys.stdin: input, from the keyboard or from a pipe (cat data.txt | python script.py).sys.stdout: normal output;printwrites here by default.sys.stderr: errors, warnings and progress messages.
Keep the two outputs separate. When a user runs python report.py > out.csv, only stdout goes to the file; messages on stderr still reach the terminal and don't corrupt the data. Write to stderr with print("warning: ...", file=sys.stderr).
Reading input:
for line in sys.stdin:processes input line by line without loading it all; each line keeps its\n, so callline.rstrip("\n").sys.stdin.read()reads everything at once.input()reads one line from stdin and strips the newline.
Buffering matters: stdout is line-buffered in a terminal but block-buffered when redirected to a file or pipe, so output may appear late. Use print(..., flush=True) or sys.stdout.flush() for progress output; stderr is always line-buffered. sys.stdout.reconfigure(line_buffering=True) changes the mode.
For binary data use the underlying buffers, sys.stdin.buffer and sys.stdout.buffer, which read and write bytes.
Example
import subprocess
import sys
script = '''
import sys
total = 0
for line in sys.stdin:
line = line.rstrip("\\n")
if not line.isdigit():
print(f"skipping {line!r}", file=sys.stderr)
continue
total += int(line)
print(total)
'''
r = subprocess.run([sys.executable, "-c", script], input="4\nx\n6\n",
capture_output=True, text=True)
print(r.stdout.strip()) # 10
print(r.stderr.strip()) # skipping 'x'Remember
- stdout is for results, stderr for messages, so redirection stays clean
for line in sys.stdinstreams input; lines keep their newline- Redirected stdout is block-buffered; flush progress output explicitly
Watch out for
- Printing progress messages to stdout in a tool whose output is piped into another program.