Zum Inhalt springen
aviral gupta

// I4.2 · ca. 35 Min. · Aufbau

Kommandozeilenprogramme mit argparse und sys

Nach dieser Lektion machen Sie aus einem Skript ein Kommandozeilenprogramm: Es liest Argumente und Optionen mit argparse, meldet Fehler auf stderr und endet mit dem passenden Exit-Status.

Lektion 2 von 6 in I4 Die Standardbibliothek für echte Programme

Danach können Sie

  • Positionsargumente, Optionen mit Typ und Standardwert sowie Schalter mit argparse definieren
  • sys.argv oder eine übergebene Liste parsen und die Ergebnisse über Attributnamen lesen
  • Fehler auf stderr melden und mit sys.exit den Exit-Status 0, 1 oder 2 setzen
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen aus der Lektion über Module: Diese Datei wird mit python main.py gestartet. Was gibt sie aus?

    print(__name__)
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen. Die Liste steht für die Kommandozeile greet.py Ada --times 3. Was gibt das aus?

    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("name")
    parser.add_argument("--times", type=int, default=1)
    args = parser.parse_args(["Ada", "--times", "3"])
    print(args.name * args.times)
  3. Üben · Aufgabe 3 von 7

    Setzen Sie die action ein, damit --verbose ein Schalter ist: True, wenn angegeben, False, wenn weggelassen.

    parser.add_argument("--verbose", action="____")
    action="")
  4. Üben · Aufgabe 4 von 7

    sys.exit kann eine Meldung annehmen. Welchen Exit-Status sieht die Shell, wenn dieses Programm endet?

    import sys
    
    sys.exit("disk full")
  5. Üben · Aufgabe 5 von 7

    Ordnen Sie jedem add_argument-Aufruf seine Bedeutung zu.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Jemand wollte eine Ja/Nein-Option und schrieb type=bool. Was gibt das aus?

    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--debug", type=bool)
    args = parser.parse_args(["--debug", "False"])
    print(args.debug)
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe, auf Ihrem Rechner. Schreiben Sie temp.py: Es nimmt eine Temperatur in Celsius als Positionsargument (float) und gibt sie in Fahrenheit aus, etwa 212.0 F. Mit --kelvin gibt es stattdessen Kelvin aus, etwa 273.15 K. Unter -273.15 schreibt es einen Fehler nach stderr und endet mit Exit-Status 1. Probieren Sie python temp.py 100, python temp.py 0 --kelvin und python temp.py abc.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Wortzähler für die Kommandozeile

wordcount zählt die Wörter eines Texts, mit einer Option --min-length und einem Schalter -u. Damit Sie mehrere Kommandozeilen auf einmal sehen, ruft demo() main mit Argumentlisten auf und zeigt den Exit-Status; stderr landet ebenfalls in der Ausgabe. Ersetzen Sie auf Ihrem Rechner die demo-Aufrufe durch sys.exit(main()) und starten Sie python main.py "the cat sat" --min-length 3.

main.py

import argparse
import shlex
import sys
from contextlib import redirect_stderr


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="wordcount", description="Count the words in a text.")
    parser.add_argument("text", help="the text to count")
    parser.add_argument("--min-length", type=int, default=1, help="skip shorter words")
    parser.add_argument("-u", "--upper", action="store_true", help="print the words in upper case")
    args = parser.parse_args(argv)  # None: read sys.argv[1:]

    words = [word for word in args.text.split() if len(word) >= args.min_length]
    if not words:
        print("wordcount: no words left", file=sys.stderr)
        return 1
    shown = " ".join(words).upper() if args.upper else " ".join(words)
    print(f"{len(words)} words: {shown}")
    return 0


def demo(argv: list[str]) -> None:
    """Runs main as if typed in a terminal; stderr shows up in the output too."""
    print(f"$ python main.py {shlex.join(argv)}".rstrip())
    with redirect_stderr(sys.stdout):
        try:
            status = main(argv)
        except SystemExit as stop:  # argparse exits on --help and on bad input
            status = stop.code if isinstance(stop.code, int) else 1
    print("exit status", status)


if __name__ == "__main__":
    # On your computer, replace these demos with: sys.exit(main())
    demo(["the cat sat on the mat", "--min-length", "3"])
    demo(["-u", "hello world"])
    demo(["a b", "--min-length", "5"])
    demo(["hello", "--min-length", "three"])
    demo([])
    demo(["--help"])

Ausführen mit

python main.py

Ausgabe

$ python main.py 'the cat sat on the mat' --min-length 3
5 words: the cat sat the mat
exit status 0
$ python main.py -u 'hello world'
2 words: HELLO WORLD
exit status 0
$ python main.py 'a b' --min-length 5
wordcount: no words left
exit status 1
$ python main.py hello --min-length three
usage: wordcount [-h] [--min-length MIN_LENGTH] [-u] text
wordcount: error: argument --min-length: invalid int value: 'three'
exit status 2
$ python main.py
usage: wordcount [-h] [--min-length MIN_LENGTH] [-u] text
wordcount: error: the following arguments are required: text
exit status 2
$ python main.py --help
usage: wordcount [-h] [--min-length MIN_LENGTH] [-u] text

Count the words in a text.

positional arguments:
  text                  the text to count

options:
  -h, --help            show this help message and exit
  --min-length MIN_LENGTH
                        skip shorter words
  -u, --upper           print the words in upper case
exit status 0
  • --min-length wird als args.min_length gelesen, und type=int hat daraus eine Zahl gemacht.
  • Der eigene Fehler gibt 1 zurück; Fehler, die argparse findet, enden mit 2, bevor main weiterläuft.
  • argparse hat die Kurzhilfe und den ganzen --help-Text aus den add_argument-Aufrufen geschrieben.
  • prog="wordcount" legt den Namen in den Meldungen fest; ohne es nimmt argparse den Skriptnamen.

Übungen

Übung 1 von 2

Ein Begrüßungswerkzeug

Vervollständigen Sie main(argv) in greet.py (hier main.py). Es nimmt einen Namen, eine Option --times (int, Standard 1) und einen Schalter --shout. Es gibt Hello, <name>! so oft aus, wie --times sagt, mit --shout in Großbuchstaben, und gibt 0 zurück. Ein --times, das keine Zahl ist, muss mit dem Exit-Status 2 von argparse enden. Führen Sie die Tests aus und probieren Sie dann python main.py Ada --times 3 --shout auf Ihrem Rechner (python3 unter macOS und Linux).

Diese Übung braucht Python auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.

Hinweise
  1. Hinweis 1

    parser.add_argument("--times", type=int, default=1) macht args.times zu einem int, der ohne Angabe 1 ist.

  2. Hinweis 2

    action="store_true" macht --shout zum Schalter; prüfen Sie ihn mit if args.shout:.

  3. Hinweis 3

    Bauen Sie die Begrüßung einmal, rufen Sie beim Schreien .upper() auf und geben Sie sie in for _ in range(args.times): aus.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import argparse
import sys


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="greet")
    parser.add_argument("name")
    parser.add_argument("--times", type=int, default=1)
    parser.add_argument("--shout", action="store_true")
    args = parser.parse_args(argv)
    greeting = f"Hello, {args.name}!"
    if args.shout:
        greeting = greeting.upper()
    for _ in range(args.times):
        print(greeting)
    return 0


if __name__ == "__main__":
    sys.exit(main())
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

import argparse
import sys


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="greet")
    parser.add_argument("name")
    # Add --times (an int, default 1) and the flag --shout.
    args = parser.parse_args(argv)
    print(f"Hello, {args.name}!")
    return 0


if __name__ == "__main__":
    sys.exit(main())

test_main.py

import contextlib
import io

from main import main


def run(argv):
    out = io.StringIO()
    err = io.StringIO()
    with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
        try:
            status = main(argv)
        except SystemExit as stop:
            status = stop.code
    return status, out.getvalue().splitlines(), err.getvalue()


def test_default():
    """Ada allein ergibt eine Begrüßung und Status 0"""
    status, lines, err = run(["Ada"])
    assert (status, lines) == (0, ["Hello, Ada!"]), f"main(['Ada']) gab {status!r} zurück und {lines!r} aus; stderr: {err!r}"


def test_times():
    """--times 3 gibt die Begrüßung dreimal aus"""
    status, lines, err = run(["Ada", "--times", "3"])
    assert lines == ["Hello, Ada!"] * 3, f"mit --times 3 gab das Programm {lines!r} aus; stderr: {err!r}"


def test_shout():
    """--shout schreibt groß, vor oder nach dem Namen"""
    status, lines, err = run(["--shout", "Bo"])
    assert lines == ["HELLO, BO!"], f"mit --shout gab das Programm {lines!r} aus; stderr: {err!r}"


def test_bad_times():
    """--times many endet mit Exit-Status 2"""
    status, lines, err = run(["Ada", "--times", "many"])
    assert status == 2, f"mit --times many war der Status {status!r}, erwartet 2 (lassen Sie type=int ablehnen)"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 2 von 2

Durchschnitt mit Exit-Status

main.py bildet den Durchschnitt der Zahlen auf seiner Kommandozeile, mit --max (Standard 100). Korrigieren Sie es: Liegt eine Zahl über dem Maximum, schreiben Sie average: 500.0 is above the maximum 100.0 (mit den echten Werten) nach stderr, nichts nach stdout, und geben 1 zurück. Sonst geben Sie den Durchschnitt mit zwei Nachkommastellen aus und geben 0 zurück. Als Programm gestartet muss der Status die Shell erreichen: Enden Sie mit sys.exit(main()).

Diese Übung braucht Python auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.

Hinweise
  1. Hinweis 1

    print(..., file=sys.stderr) schickt eine Zeile nach stderr statt nach stdout.

  2. Hinweis 2

    Geben Sie direkt nach der Fehlermeldung 1 zurück, damit der Durchschnitt nie ausgegeben wird.

  3. Hinweis 3

    main() gibt den Status zurück, aber erst sys.exit(main()) reicht ihn an die Shell weiter.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import argparse
import sys


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="average")
    parser.add_argument("numbers", nargs="+", type=float)
    parser.add_argument("--max", type=float, default=100.0)
    args = parser.parse_args(argv)
    for number in args.numbers:
        if number > args.max:
            print(f"average: {number} is above the maximum {args.max}", file=sys.stderr)
            return 1
    print(f"{sum(args.numbers) / len(args.numbers):.2f}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

import argparse
import sys


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="average")
    parser.add_argument("numbers", nargs="+", type=float)
    parser.add_argument("--max", type=float, default=100.0)
    args = parser.parse_args(argv)
    for number in args.numbers:
        if number > args.max:
            print(f"average: {number} is above the maximum {args.max}")
    print(f"{sum(args.numbers) / len(args.numbers):.2f}")
    return 0


if __name__ == "__main__":
    main()

test_main.py

import contextlib
import io
import sys

from learnrun import run_main
from main import main


def run(argv):
    out = io.StringIO()
    err = io.StringIO()
    with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
        try:
            status = main(argv)
        except SystemExit as stop:
            status = stop.code
    return status, out.getvalue(), err.getvalue()


def test_average():
    """2, 4 und 9 ergeben 5.00 und Status 0"""
    status, out, err = run(["2", "4", "9"])
    assert (status, out.strip()) == (0, "5.00"), f"main gab {status!r} zurück und {out!r} aus"


def test_above_max():
    """500 wird auf stderr gemeldet, mit Status 1 und leerem stdout"""
    status, out, err = run(["5", "500"])
    assert status == 1, f"main gab {status!r} zurück, erwartet 1"
    assert out == "", f"stdout sollte leer sein, enthält aber {out!r}"
    assert err.strip() == "average: 500.0 is above the maximum 100.0", f"stderr enthält {err!r}"


def test_custom_max():
    """--max 10 weist 11 ab"""
    status, out, err = run(["--max", "10", "5", "11"])
    assert status == 1, f"mit --max 10 und 11 gab main {status!r} zurück, erwartet 1"


def test_not_a_number():
    """Ein Wort statt einer Zahl endet mit Status 2"""
    status, out, err = run(["five"])
    assert status == 2, f"für five war der Status {status!r}, erwartet 2"


def test_status_reaches_the_shell():
    """Als Programm endet python main.py 5 500 mit Status 1"""
    saved = sys.argv
    sys.argv = ["main.py", "5", "500"]
    code = 0
    try:
        with contextlib.redirect_stderr(io.StringIO()):
            run_main()
    except SystemExit as stop:
        code = stop.code
    finally:
        sys.argv = saved
    assert code == 1, f"das Programm endete mit Status {code!r}, erwartet 1: rufen Sie sys.exit(main()) auf"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Häufige Fehler

type=int vergessen

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("count")
args = parser.parse_args(["3"])
print(args.count + 1)

Was Python ausgibt

TypeError: can only concatenate str (not "int") to str

Warum, und die Lösung

Jedes Kommandozeilenargument kommt als String an, und argparse lässt es ohne type dabei. Schreiben Sie add_argument("count", type=int). Dann wandelt argparse den Wert um und weist etwas wie three mit einer klaren Meldung und Exit-Status 2 ab.

Eine Option mit Bindestrich im Namen lesen

import argparse

parser = argparse.ArgumentParser(prog="wordcount")
parser.add_argument("--min-length", type=int, default=1)
args = parser.parse_args(["--min-length", "3"])
print(args.min-length)

Was Python ausgibt

AttributeError: 'Namespace' object has no attribute 'min'

Warum, und die Lösung

args.min-length liest Python als args.min minus length. argparse macht aus Bindestrichen im Optionsnamen Unterstriche, der Wert steht also in args.min_length.

Ein Pflichtargument weglassen

import argparse

parser = argparse.ArgumentParser(prog="greet")
parser.add_argument("name")
args = parser.parse_args([])
print("Hello,", args.name)

Was Python ausgibt

greet: error: the following arguments are required: name

Warum, und die Lösung

Ein Positionsargument muss angegeben werden. argparse schreibt die Kurzhilfe und diesen Fehler nach stderr und endet mit Status 2; print wird nie erreicht. Soll es optional sein, machen Sie eine Option mit Standardwert daraus, etwa add_argument("--name", default="world").

Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Von sys.argv zu argparse

python greet.py Ada --times 3 legt die Wörter des Befehls in sys.argv ab: ["greet.py", "Ada", "--times", "3"]. argv[0] ist das Skript, und jedes Element ist ein String. argparse liest diese Liste für Sie: add_argument("name") ist ein Positionsargument und Pflicht. add_argument("--times", type=int, default=1) ist eine Option, die type umwandelt und die ohne Angabe default erhält. action="store_true" macht einen Schalter: True, wenn angegeben, sonst False. nargs="+" sammelt einen oder mehr Werte in einer Liste. --help schreibt argparse gleich mit.

Ein testbares main(argv)

parse_args(argv) parst die übergebene Liste; mit None liest es sys.argv[1:]. Schreiben Sie also def main(argv: list[str] | None = None) -> int und beenden Sie die Datei mit if __name__ == "__main__": sys.exit(main()). Im Terminal liest main die echte Kommandozeile; im Test läuft main(["Ada", "--times", "3"]) mit einer Liste. Die Ergebnisse sind Attribute: args.name, args.times. Bindestriche in einer Option werden zu Unterstrichen, --min-length lesen Sie also als args.min_length.

Exit-Status und stderr

Endet ein Programm, bekommt die Shell seinen Exit-Status: 0 heißt Erfolg, alles andere Fehler. Unix-Programme nutzen 2 für eine falsche Kommandozeile und 1 für andere Fehler. argparse hält sich daran: Bei einem fehlenden oder ungültigen Argument schreibt es die Kurzhilfe und einen Fehler nach stderr und endet mit 2. Eigene Fehler geben Sie mit print(..., file=sys.stderr) aus und geben 1 aus main zurück. sys.exit(status) löst SystemExit aus. Fehler auf stderr bleiben sichtbar, auch wenn die Ausgabe mit > out.txt gespeichert wird.

Quellen

Zuletzt geprüft am 29. September 2026