Command-Line Streams and Exit Status in Python
Read command-line arguments, handle standard input and output streams, and return meaningful process exit statuses with Python’s sys module.
Intermediate
A command-line program has a small interface with the environment that starts it: it receives argument tokens, may read text from standard input, writes normal output and diagnostics to different streams, and finishes with an exit status. The sys module exposes these process-facing pieces. This lesson covers that boundary; argparse remains the better starting point for a structured command-line interface, and subprocess covers launching a separate child program.
Inspect the arguments your program receives
sys.argv is a list of strings supplied when the Python program starts. Item 0 is the script name (or another launch-specific value); sys.argv[1:] contains the arguments intended for your program. Tokens are not automatically converted or validated. For real options, defaults, help, and validation, let argparse own the interface instead of growing a fragile manual parser.
Output
Ready to run.
Use standard input for piped text
sys.stdin is a text stream, like a file object. Use input() for one prompted response, but iterate over sys.stdin when a program should accept multiple lines from a pipe or redirected file. Each line normally includes its trailing newline; writing transformed lines with sys.stdout.write() preserves that newline without adding another one. The same processing function can accept any file-like text stream. This runnable example uses io.StringIO for repeatable sample input; a real command-line program can pass sys.stdin to the function instead.
Output
Ready to run.
Keep normal output and diagnostics separate
Write results intended for a person or another program to sys.stdout. Write warnings and error messages to sys.stderr, often with print(..., file=sys.stderr). Separating these streams lets a caller redirect or pipe the result without accidentally treating a diagnostic as data. Both are text streams and may be redirected by a test runner or application, so library code should not assume they are always real terminal streams.
Output
Ready to run.
Return a meaningful exit status
An exit status is a small signal to the shell, automation, or a parent process. Conventionally, 0 means success and a nonzero value means that the requested work did not complete successfully. Have a main() function return the status, and keep its decision-making testable. Do not use a nonzero status merely to report a harmless warning.
Output
Ready to run.
Use SystemExit at the program boundary
At the script entry point, raise SystemExit(main()) to turn main()’s return value into the process exit status. sys.exit(code) does the same by raising SystemExit; it is not an immediate low-level termination call, so normal finally-block cleanup still runs. Passing a string prints a message to standard error and exits with status 1. Avoid catching BaseException broadly, which can intercept SystemExit and KeyboardInterrupt.
Output
Ready to run.
Quick Check
Quick check
Which part of sys.argv contains the arguments intended for the program rather than the launch entry?
import sys
print(sys.argv[1:])
Quick check
Where should a command write a diagnostic that should not be mixed into normal output?
import sys
print("Record skipped", file=sys.stderr)
Quick check
Which stream is designed for reading multiple lines piped into a command?
from io import StringIO
source = StringIO("foxipa\npython\n")
print(sum(1 for _ in source))
Quick check
What does sys.exit(2) do in an ordinary script when the exception is not caught?
import sys
try:
sys.exit(2)
except SystemExit as request:
print(request.code)
Quick check
What is a good design for a command that needs validation, help, and named options?
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("count", type=int)
args = parser.parse_args(["3"])
print(args.count)
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.