Wenn du eine Pythonanwendung für die Kommandozeile entwickelst, wollen deine User früher oder später die Möglichkeit ihre Parameter direkt beim Programmstart mitgeben können. In diesem Beitrag findest du ein paar Beispiele, wie du Argumente/Parameter aus der Kommandozeile in Python verarbeiten kannst.

Parameter sind die möglichen Werte, die ein Programm verarbeiten kann. Argumente hingegen sind nur die Werte, die tatsächlich vom User übergeben werden.

In diesem Beitrag erfährst du zunächst, wie du mit dem sys Modul deinen eigenen Parser bauen könntest und warum das keine gute Idee ist. Im anschließenden Teil stelle ich dir das build-in Modul argparse vor, das seit Python 3.2 dabei ist.

Viel Spaß!

Manuell mit sys

Wenn du es nicht kompliziert magst kannst du einfach das sys Modul verwenden um alle Argumente als Liste von Strings zu erhalten und von Hand verarbeiten.

test.py:

import sys
print (sys.argv)
python test.py --test 123
['test.py', '--test', '123']

Das Problem an diesem manuellen Parsing ist, dass es in großen Projekten sehr schnell sehr viele Parameter gibt. Das kann kompliziert werden.

Wenn du dir zum Beispiel das vermeintlich einfache Linux Tool ls anschaust, mit dem du dir den Inhalt eines Ordners anzeigen lassen kannst… Dann versteht dieses um die 60 verschiedenen Parameter. Manche davon sind sogar mit mehreren Namen erreichbar. So ist zum Beispiel das Flag “–all” identisch mit “-a”.

Andere Parameter erwarten einen zweiten Parameter, also einen Parameter der direkt hinter dem ersten folgt. Dieser zweite Parameter darf dann natürlich nicht als normaler Parameter gewertet werden da es sonst zu Problemen führen kann.

Manchmal kommt es vor, dass ein User ein Argument verwendet, das es eigentlich nicht als Parameter gibt. Im Idealfall willst du diesen User warnen damit er nicht verwirrt ist wenn etwas nicht so funktioniert wie er sich das gedacht hat.

Zu guterletzt solltest du dann noch die eine Hilfe für dein Programm schreiben womit der Benutzer die möglichen Parameter nachschlagen kann.

All diese Aufgaben nimmt dir das argparse Modul sehr elegant ab!

Automatisch mit argparse

Mit argparse kannst du die unterschiedlichen Argumententypen im Quellcode definieren und argparse baut dir automatisch eine Hilfedatei aus deinem Quellcode.

Hier ein Beispiel von einem Programm komplett ohne Argumente.

test.py:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
args = parser.parse_args()

In args, werden alle Parameter die der User mitgibt gespeichert. Wie zu erwarten passiert in diesem Beispiel nichts wenn du keine Argumente mitgibst:

python test.py

argparse erstellt automatisch ein optionales -h bzw. –help Argument, mit dem eine automatisch generierte Hilfe angezeigt wird. Dort siehst du 1) die Usage in kurzer Schreibweise 2) die Beschreibung aus dem “Description” Parameter und 3) eine Liste von allen Argumenten und deren Beschreibung:

python test.py -h
usage: test.py [-h]

Dies ist mein erstes Programm.

optional arguments:
  -h, --help  show this help message and exit

Wenn der User ein Argument verwendet, welches du nicht definiert hast, bekommt er eine entsprechende Fehlermeldung angezeigt:

python test.py --kekse
usage: test.py [-h]
test.py: error: unrecognized arguments: --kekse

Flags

–help ist ein sogenanntes Flag. Also ein boolscher Parameter, der entweder gesetzt sein kann (True) oder nicht gesetzt sein kann (False).

Wenn du ein eigenes Flag hinzuzufügen willst geht das mit der add_argument() Methode. Im folgenden fügen wir das -v Flag hinzu.

test.py:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
parser.add_argument(
        "-v",
        "--verbose",
        help="Verbose, use this flag for debugging.",
        action="store_true",
    )
args = parser.parse_args()
print (args.verbose)
python test.py -v
True

Das args Objekt ist vom Typ argparse.Namespace. Du kannst es wie ein Named Tuple verwenden um auf deine Parameter zuzugreifen. In diesem Fall ist args.verbose True wenn das -v bzw. –verbose Flag gesetzt ist und False wenn es nicht gesetzt ist.

python test.py
False

Der Parameter action="store_true" bewirkt, dass es sich um ein Flag und nicht um einen Wert handelt.

Parameter mit Wert

Einen Parameter mit einem Wert zu erwarten funktioniert sehr ähnlich:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
parser.add_argument(
        "-u",
        "--username",
        help="Valid username",
    )
args = parser.parse_args()
print (args.username)
python test.py -u quisl
quisl

Und das passiert wenn der User den Wert vergisst:

python test.py -u
usage: test.py [-h] [-u USERNAME]
test.py: error: argument -u/--username: expected one argument

Parameter mit mehreren Werten

Wenn du mehrere Werte bei einem Parameter erwarten willst, kannst du das mit “nargs” tun:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
parser.add_argument(
        "-l",
        "--list",
        nargs="+",
        help="woerterliste",
    )
args = parser.parse_args()
print (args.list)
python test.py --list abc def
['abc', 'def']

Anstelle von “+” kannst du auch eine bestimmte Anzahl mitgeben. Hier eine Übersicht:

nargs Beschreibung
1 Genau 1 Wert
2 Genau 2 Wert
“+” 1 bis unendlich (∞)
“*” 0 bis unendlich (∞)

Zahlen müssen als Integer übergeben werden, nicht als String!

Auswahl aus vordefinierten Werten

Mit “Choices” kannst du bestimmte Werte vorgeben. Dazu kann jede Liste und Generator dienen:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
parser.add_argument(
        "-m",
        "--move",
        choices=['papier', 'stein', 'schere'],
        help="Choose a move",
    )
args = parser.parse_args()
print (args.move)
python test.py -m stein
stein

Gleichzeitig wird der User gewarnt, wenn er etwas falsches gibt.

python test.py -m pferd
usage: test.py [-h] [-m {papier,stein,schere}]
test.py: error: argument -m/--move: invalid choice: 'pferd' (choose from 'papier', 'stein', 'schere')

Aktion mit Subparsern

Manche Programme haben bestimmte Kommandos die unterschiedliche Parametersets haben können. Zum Beispiel kann es ein “read” Kommando geben, und ein “write” Kommando geben, das einen Text als Parameter erwartet.

Um dem User nicht die Möglichkeit zu geben einen Read Befehl mit dem Text Parameter abzuschicken kannst du Subparser verwenden. Hier ein Beispiel mit read(path) und write(path, text):

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')

subparsers = parser.add_subparsers(help='sub-command help', dest="parsername")

# parser fuer das "read" Kommando
parser_read = subparsers.add_parser('read', help='read help')
parser_read.add_argument(
        "-p",
        "--path",
        help="path for a file",
    )

# parser fuer das "write" Kommando
parser_write = subparsers.add_parser('write', help='write help')
parser_write.add_argument(
        "-p",
        "--path",
        help="path for a file",
    )
parser_write.add_argument(
        "-t",
        "--text",
        help="text to write",
    )

args = parser.parse_args()
print (args)
python test.py write --path /a/b --text abcdefg
Namespace(parsername='write', path='/a/b', text='abcdefg')

oder read:

python test.py read --path /a/b
Namespace(parsername='read', path='/a/b')

Wie du siehst, wird immer nur der gewählte Subparser verarbeitet. Die Hilfedatei sieht wie folgt aus:

python test.py --help
usage: test.py [-h] {read,write} ...

Dies ist mein erstes Programm.

positional arguments:
  {read,write}  sub-command help
    read        read help
    write       write help

optional arguments:
  -h, --help    show this help message and exit

Oder nur den read Parser:

python test.py read --help
usage: test.py read [-h] [-p PATH]

optional arguments:
  -h, --help            show this help message and exit
  -p PATH, --path PATH  path for a file

Optional vs required

Bisher waren hier alle Beispiele optionale Werte. Optionale Werte können einen Defaultwert haben. Dieser wird verwendet wenn der User einen Parameter nicht als Argument mitgibt. Du kannst diese mit “default” definieren:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
parser.add_argument(
        "-u",
        "--username",
        default="forsen",
        help="Valid username",
    )
args = parser.parse_args()
print (args.username)
python test.py
forsen

Alternativ kannst du einen Parameter auch als zwingend benötigt, also “required” definieren. Dann gibt es einen Fehler wenn er nicht vorhanden ist:

import argparse
parser = argparse.ArgumentParser(description='Dies ist mein erstes Programm.')
parser.add_argument(
        "-u",
        "--username",
        required=True,
        help="Valid username",
    )
args = parser.parse_args()
print (args.username)
python test.py
usage: test.py [-h] -u USERNAME
test.py: error: the following arguments are required: -u/--username

Offizielle Dokumentation

Dieser Beitrag soll nur eine Einführung gewesen sein. Es gibt noch viel mehr Mölichkeiten. Guck dir dazu am besten mal die offizielle Dokumentation an!


Konnte ich helfen? Ich freue mich über einen Drink! 💙