C.W.K.
Stream
Lesson 07 of 07 · published

Input, Output, and Running Scripts — Becoming a Real Program

~22 min · print, input, sys.argv, stdin, stdout, stderr, main, scripts

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

print() — more options than you thought

print() is the first function every Python programmer meets, and most never look past print("hello"). It has four optional keyword arguments worth knowing:

  • sep — the separator between multiple arguments. Default is a single space. print("a", "b", sep="|") prints a|b.
  • end — what gets appended after the output. Default is "\n" (newline). print("hi", end="") prints hi with no trailing newline.
  • file — where to write. Default is sys.stdout. Set to sys.stderr for error messages, or to any open file object.
  • flush — force the output to appear immediately. Useful for progress bars and piped output.

input() — reading a line from the user

input(prompt) shows the prompt, waits for the user to type a line, and returns whatever they typed as a string. Always a string — if you need a number, you have to convert: int(input("age: ")).

If standard input reaches end-of-file (Ctrl-D on Unix, Ctrl-Z + Enter on Windows), input() raises EOFError. Wrap in try/except if your script might be piped input that runs out: try: line = input() except EOFError: ....

sys.argv — your first command-line interface

When you run python myscript.py foo bar baz, the values ["myscript.py", "foo", "bar", "baz"] end up in sys.argv. sys.argv[0] is always the script name; sys.argv[1:] is the actual arguments.

For one-off scripts, this is enough. For real CLI tools — argument parsing, help text, type coercion, subcommands — you'll want argparse or typer (covered in the CLI track). But knowing sys.argv directly is foundational; every higher-level CLI library is built on top.

script vs module — the __name__ == "__main__" idiom

Every Python file has a built-in variable __name__. When you run the file directly (python myscript.py), __name__ is the string "__main__". When the file is imported by another module, __name__ is the module's name (the filename without .py).

This enables a critical idiom — the if __name__ == "__main__": block. Code inside it runs only when the file is executed directly, not when it's imported. This lets you write a file that's both a usable script and an importable library, without weird side effects on import.

Principle: Every Python script you write that does anything substantial should put its execution code under if __name__ == "__main__":. The functions and classes can sit at module top level (so they're importable). The actual run-this-thing logic gets gated. This costs nothing and saves you the day someone (or a future you) wants to import a function from your script.

The three standard streams — stdin, stdout, stderr

Every running program has three default streams, available as sys.stdin, sys.stdout, sys.stderr. print() writes to stdout by default. input() reads from stdin. Errors and warnings should go to stderr — that way pipes and redirects don't mix them with normal output.

This matters because Unix pipes only carry stdout. python myscript.py | grep hello grep's only the stdout. Errors go to stderr and stay visible on the terminal even when output is piped or redirected to a file.

Self-reference: cwkPippa's CLI scripts (under scripts/) all follow the same pattern — normal output to stdout, status messages and warnings to stderr, exit code 0 for success and non-zero for failure. This lets the heartbeat scheduler tell, automatically, whether a job ran cleanly. Without that discipline the scheduler would have to parse logs to know what worked.

Running scripts — the four ways

Once you have a Python file, four common ways to run it:

  • python myscript.py — explicit, works everywhere
  • python -m mymodule — runs a module as a script (uses Python's import machinery)
  • ./myscript.py — needs a shebang line (#!/usr/bin/env python3) at the top and the file marked executable (chmod +x myscript.py). Unix/macOS only.
  • python -c "print('hello')" — runs an inline command without a file. Useful for one-liners and Makefile snippets.

Bottom line

You can now write a Python script that takes command-line arguments, reads from stdin, writes to stdout and stderr appropriately, and is also importable as a library. That's the shape of a real Python program. Lesson 8 onwards builds on top of that shape.

Pythonic Way: Always use if __name__ == "__main__": for script entry points. Always send errors to stderr (print(..., file=sys.stderr)). Always exit with sys.exit(1) on failure, not just return. Tools that respect Unix conventions compose with other tools; tools that don't become islands.

Code

print() — all four optional arguments·python
>>> print("a", "b", "c")                         # default sep=' ', end='\n'
a b c

>>> print("a", "b", "c", sep="|")
a|b|c

>>> print("loading", end="")                     # no newline — useful for progress
loading
>>> for i in range(3):
...     print(".", end="", flush=True)            # flush forces immediate output
...
...

>>> import sys
>>> print("oops", file=sys.stderr)                # error message to stderr
oops
input() — read a line, always returns str·python
name = input("What's your name? ")
age_str = input("How old? ")
age = int(age_str)               # input is always str — convert if needed

print(f"Hi {name}, you'll be {age + 1} next year.")

# Handling EOF (e.g. piped input that runs out):
try:
    line = input()
except EOFError:
    print("No more input.", file=sys.stderr)
sys.argv — first contact with command-line args·python
# greet.py
import sys

if len(sys.argv) < 2:
    print(f"Usage: {sys.argv[0]} <name>", file=sys.stderr)
    sys.exit(1)

name = sys.argv[1]
print(f"Hello, {name}!")

# Run it:
# $ python greet.py Pippa
# Hello, Pippa!
# $ python greet.py
# Usage: greet.py <name>
# $ echo $?      # exit code
# 1
__name__ == '__main__' — script + library in one file·python
# greeter.py
def make_greeting(name):
    """Return a friendly greeting."""
    return f"Hello, {name}!"

def main():
    import sys
    if len(sys.argv) > 1:
        print(make_greeting(sys.argv[1]))
    else:
        print(make_greeting("world"))

if __name__ == "__main__":
    main()

# Now this file works two ways:
# (1) As a script:        python greeter.py Pippa
# (2) As a library:       from greeter import make_greeting
#                         make_greeting("Pippa")  # 'Hello, Pippa!'
stdin / stdout / stderr — Unix-style composition·python
# uppercase.py — reads stdin, writes uppercased to stdout
import sys

for line in sys.stdin:
    print(line.rstrip().upper())

# Compose with other Unix tools:
# $ echo 'hello world' | python uppercase.py
# HELLO WORLD

# $ cat names.txt | python uppercase.py | sort
# (full Unix pipeline — your Python script is just one stage)

# Errors go to stderr — visible even when stdout is redirected:
# $ python uppercase.py < input.txt > output.txt
# (any prints to sys.stderr still appear on your terminal)

External links

Exercise

Write a small script repeat.py that takes two command-line arguments — a string and a number — and prints the string that many times, one per line. Use sys.argv directly (no argparse yet). Add an if __name__ == "__main__": guard. Add error handling that prints to sys.stderr and exits with code 1 if the arguments are wrong (missing or non-integer). Then test it: pipe its output into wc -l and verify the count matches the number you passed.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.