Wie lassen sich Befehlszeilenargumente am besten analysieren?
-
09-06-2019 - |
Frage
Was ist das? am einfachsten, am besten, und die meisten flexibel Methode oder Bibliothek zum Parsen von Python-Befehlszeilenargumenten?
Lösung
Diese Antwort legt nahe optparse
was für ältere Python-Versionen geeignet ist.Für Python 2.7 und höher: argparse
ersetzt optparse
.Sehen diese Antwort für mehr Informationen.
Wie andere Leute bereits betont haben, ist es besser, sich für optparse statt für getopt zu entscheiden.getopt ist so ziemlich eine Eins-zu-eins-Zuordnung der Standardfunktionen der getopt(3)-C-Bibliothek und nicht sehr einfach zu verwenden.
optparse ist zwar etwas ausführlicher, aber viel besser strukturiert und lässt sich später einfacher erweitern.
Hier ist eine typische Zeile zum Hinzufügen einer Option zu Ihrem Parser:
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
Es spricht so ziemlich für sich;Zur Verarbeitungszeit akzeptiert es -q oder --query als Optionen, speichert das Argument in einem Attribut namens query und hat einen Standardwert, wenn Sie ihn nicht angeben.Es ist außerdem selbstdokumentierend, da Sie das Hilfeargument (das bei der Ausführung mit -h/--help verwendet wird) direkt dort mit der Option deklarieren.
Normalerweise analysieren Sie Ihre Argumente mit:
options, args = parser.parse_args()
Dadurch werden standardmäßig die an das Skript übergebenen Standardargumente analysiert (sys.argv[1:]).
options.query wird dann auf den Wert gesetzt, den Sie an das Skript übergeben haben.
Sie erstellen einen Parser einfach dadurch
parser = optparse.OptionParser()
Das sind alle Grundlagen, die Sie brauchen.Hier ist ein vollständiges Python-Skript, das dies zeigt:
import optparse
parser = optparse.OptionParser()
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
options, args = parser.parse_args()
print 'Query string:', options.query
5 Python-Zeilen, die Ihnen die Grundlagen zeigen.
Speichern Sie es in „sample.py“ und führen Sie es einmal mit aus
python sample.py
und einmal mit
python sample.py --query myquery
Darüber hinaus werden Sie feststellen, dass optparse sehr einfach zu erweitern ist.In einem meiner Projekte habe ich eine Command-Klasse erstellt, mit der Sie Unterbefehle einfach in einem Befehlsbaum verschachteln können.Es verwendet optparse stark, um Befehle miteinander zu verketten.Es ist nicht etwas, das ich einfach in ein paar Zeilen erklären kann, aber Sie können es gerne tun Stöbern Sie in meinem Repository für die Hauptklasse, sowie eine Klasse, die es verwendet, und die Option Parser
Andere Tipps
Andere Antworten erwähnen das argparse
ist der richtige Weg für neues Python, aber geben Sie keine Anwendungsbeispiele an.Der Vollständigkeit halber finden Sie hier eine kurze Zusammenfassung der Verwendung von argparse:
1) Initialisieren
import argparse
# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')
2) Argumente hinzufügen
# Required positional argument
parser.add_argument('pos_arg', type=int,
help='A required integer positional argument')
# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
help='An optional integer positional argument')
# Optional argument
parser.add_argument('--opt_arg', type=int,
help='An optional integer argument')
# Switch
parser.add_argument('--switch', action='store_true',
help='A boolean switch')
3) Analysieren
args = parser.parse_args()
4) Zugang
print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)
5) Überprüfen Sie die Werte
if args.pos_arg > 10:
parser.error("pos_arg cannot be larger than 10")
Verwendung
Richtige Verwendung:
$ ./app 1 2 --opt_arg 3 --switch
Argument values:
1
2
3
True
Falsche Argumente:
$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'
$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10
Vollständige Hilfe:
$ ./app -h
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
Optional app description
positional arguments:
pos_arg A required integer positional argument
opt_pos_arg An optional integer positional argument
optional arguments:
-h, --help show this help message and exit
--opt_arg OPT_ARG An optional integer argument
--switch A boolean switch
Verwendung von docopt
Seit 2012 ist Python sehr einfach, leistungsstark und wirklich Cool Modul zum Parsen von Argumenten aufgerufen docopt.Es funktioniert mit Python 2.6 bis 3.5 und erfordert keine Installation (einfach kopieren).Hier ist ein Beispiel aus der Dokumentation:
"""Naval Fate.
Usage:
naval_fate.py ship new <name>...
naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
naval_fate.py ship shoot <x> <y>
naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.py (-h | --help)
naval_fate.py --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Naval Fate 2.0')
print(arguments)
So, das ist es:2 Zeilen Code plus Ihre Dokumentzeichenfolge Ist wesentlich und Sie erhalten Ihre Argumente analysiert und in Ihrem Argumentobjekt verfügbar.Ich habe dir gesagt, dass es cool ist, nicht wahr ;-)
Python-Fire verwenden
Seit 2017 Python-Feuer hat ein weiteres cooles Modul, das Ihrem Code eine CLI-Schnittstelle geben kann, während Sie dies tun null Argumentanalyse.Hier ist ein einfaches Beispiel aus der Dokumentation (dieses kleine Programm stellt die Funktion bereit double
zur Kommandozeile):
import fire
class Calculator(object):
def double(self, number):
return 2 * number
if __name__ == '__main__':
fire.Fire(Calculator)
Von der Befehlszeile aus können Sie Folgendes ausführen:
> calculator.py double 10
20
> calculator.py double --number=15
30
Großartig, nicht wahr?
ich bevorzuge Klicken.Es abstrahiert Verwaltungsoptionen und ermöglicht „(...) das Erstellen schöner Befehlszeilenschnittstellen auf zusammensetzbare Weise mit so wenig Code wie nötig“.
Hier ist ein Beispiel für die Verwendung:
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
Außerdem werden automatisch gut formatierte Hilfeseiten generiert:
$ python hello.py --help
Usage: hello.py [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
So ziemlich jeder nutzt es getopt
Hier ist der Beispielcode für das Dokument:
import getopt, sys
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
except getopt.GetoptError:
# print help information and exit:
usage()
sys.exit(2)
output = None
verbose = False
for o, a in opts:
if o == "-v":
verbose = True
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-o", "--output"):
output = a
Kurz gesagt, so funktioniert es.
Sie haben zwei Arten von Optionen.Diejenigen, die Argumente erhalten, und diejenigen, die genau wie Schalter sind.
sys.argv
ist so ziemlich dein char** argv
in C.Wie in C überspringen Sie das erste Element, das der Name Ihres Programms ist, und analysieren nur die Argumente: sys.argv[1:]
Getopt.getopt
analysiert es gemäß der Regel, die Sie im Argument angeben.
"ho:v"
Hier werden die kurzen Argumente beschrieben: -ONELETTER
.Der :
bedeutet, dass -o
akzeptiert ein Argument.
Endlich ["help", "output="]
beschreibt lange Argumente ( --MORETHANONELETTER
).Der =
nach der Ausgabe bedeutet wiederum, dass die Ausgabe ein Argument akzeptiert.
Das Ergebnis ist eine Liste von Paaren (Option, Argument)
Wenn eine Option kein Argument akzeptiert (wie --help
Hier das arg
Teil ist eine leere Zeichenfolge.Normalerweise möchten Sie diese Liste dann in einer Schleife durchlaufen und den Optionsnamen wie im Beispiel testen.
Ich hoffe, das hat Ihnen geholfen.
Verwenden optparse
die mit der Standardbibliothek geliefert wird.Zum Beispiel:
#!/usr/bin/env python
import optparse
def main():
p = optparse.OptionParser()
p.add_option('--person', '-p', default="world")
options, arguments = p.parse_args()
print 'Hello %s' % options.person
if __name__ == '__main__':
main()
Quelle: Verwenden von Python zum Erstellen von UNIX-Befehlszeilentools
Ab Python 2.7 ist optparse jedoch veraltet, siehe: Warum argparse anstelle von optparse verwenden?
Nur für den Fall, dass Sie es benötigen, kann dies bei Bedarf hilfreich sein greifen Unicode-Argumente unter Win32 (2K, XP usw.):
from ctypes import *
def wmain(argc, argv):
print argc
for i in argv:
print i
return 0
def startup():
size = c_int()
ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
ref = c_wchar_p * size.value
raw = ref.from_address(ptr)
args = [arg for arg in raw]
windll.kernel32.LocalFree(ptr)
exit(wmain(len(args), args))
startup()
Leichte Standardeinstellungen für Befehlszeilenargumente
Obwohl argparse
ist großartig und die richtige Antwort für vollständig dokumentierte Befehlszeilenschalter und erweiterte Funktionen. Sie können Funktionsargument-Standardeinstellungen verwenden, um unkomplizierte Positionsargumente sehr einfach zu verarbeiten.
import sys
def get_args(name='default', first='a', second=2):
return first, int(second)
first, second = get_args(*sys.argv)
print first, second
Das Argument „name“ erfasst den Skriptnamen und wird nicht verwendet.Die Testausgabe sieht folgendermaßen aus:
> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20
Für einfache Skripte, bei denen ich nur einige Standardwerte möchte, finde ich das völlig ausreichend.Möglicherweise möchten Sie auch eine Typumwandlung in die Rückgabewerte einschließen, da sonst alle Befehlszeilenwerte Zeichenfolgen sind.
Ich denke, der beste Weg für größere Projekte ist optparse, aber wenn Sie nach einem einfachen Weg suchen, vielleicht http://werkzeug.pocoo.org/documentation/script ist etwas für dich.
from werkzeug import script
# actions go here
def action_foo(name=""):
"""action foo does foo"""
pass
def action_bar(id=0, title="default title"):
"""action bar does bar"""
pass
if __name__ == '__main__':
script.run()
Grundsätzlich ist jede Funktion action_* der Befehlszeile ausgesetzt und eine nette Hilfebotschaft wird kostenlos generiert.
python foo.py
usage: foo.py <action> [<options>]
foo.py --help
actions:
bar:
action bar does bar
--id integer 0
--title string default title
foo:
action foo does foo
--name string
Ich bevorzuge optparse gegenüber getopt.Es ist sehr deklarativ:Sie teilen ihm die Namen der Optionen und die Auswirkungen mit, die sie haben sollen (z. B. das Festlegen eines booleschen Felds), und es gibt Ihnen ein Wörterbuch zurück, das gemäß Ihren Spezifikationen gefüllt ist.
Consoleargs verdient hier Erwähnung.Es ist sehr einfach zu bedienen.Hör zu:
from consoleargs import command
@command
def main(url, name=None):
"""
:param url: Remote URL
:param name: File name
"""
print """Downloading url '%r' into file '%r'""" % (url, name)
if __name__ == '__main__':
main()
Jetzt in der Konsole:
% python demo.py --help
Usage: demo.py URL [OPTIONS]
URL: Remote URL
Options:
--name -n File name
% python demo.py http://www.google.com/
Downloading url ''http://www.google.com/'' into file 'None'
% python demo.py http://www.google.com/ --name=index.html
Downloading url ''http://www.google.com/'' into file ''index.html''
Argparse-Code kann länger sein als der tatsächliche Implementierungscode!
Das ist ein Problem, das meiner Meinung nach bei den meisten gängigen Optionen zum Parsen von Argumenten besteht: Wenn Ihre Parameter nur bescheiden sind, wird der Code zu ihrer Dokumentation unverhältnismäßig groß im Vergleich zu dem Nutzen, den sie bieten.
Ein relativer Neuling in der Argument-Parsing-Szene (glaube ich). Ort.
Es macht einige anerkannte Kompromisse mit argparse, verwendet aber Inline-Dokumentation und lässt sich einfach umschließen main()
Typ Funktion Funktion:
def main(excel_file_path: "Path to input training file.",
excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
existing_model_path: "Path to an existing model to refine."=None,
batch_size_start: "The smallest size of any minibatch."=10.,
batch_size_stop: "The largest size of any minibatch."=250.,
batch_size_step: "The step for increase in minibatch size."=1.002,
batch_test_steps: "Flag. If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."
pass # Implementation code goes here!
if __name__ == '__main__':
import plac; plac.call(main)
Hier ist eine Methode, keine Bibliothek, die für mich zu funktionieren scheint.
Die Ziele hier sind, prägnant zu sein, jedes Argument wird durch eine einzelne Zeile analysiert, die Argumente werden zur besseren Lesbarkeit ausgerichtet, der Code ist einfach und hängt nicht von irgendwelchen speziellen Modulen ab (nur os + sys), warnt elegant vor fehlenden oder unbekannten Argumenten , verwendet eine einfache for/range()-Schleife und funktioniert in Python 2.x und 3.x
Dargestellt sind zwei Umschaltflags (-d, -v) und zwei durch Argumente gesteuerte Werte (-i xxx und -o xxx).
import os,sys
def HelpAndExit():
print("<<your help output goes here>>")
sys.exit(1)
def Fatal(msg):
sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
sys.exit(1)
def NextArg(i):
'''Return the next command line argument (if there is one)'''
if ((i+1) >= len(sys.argv)):
Fatal("'%s' expected an argument" % sys.argv[i])
return(1, sys.argv[i+1])
### MAIN
if __name__=='__main__':
verbose = 0
debug = 0
infile = "infile"
outfile = "outfile"
# Parse command line
skip = 0
for i in range(1, len(sys.argv)):
if not skip:
if sys.argv[i][:2] == "-d": debug ^= 1
elif sys.argv[i][:2] == "-v": verbose ^= 1
elif sys.argv[i][:2] == "-i": (skip,infile) = NextArg(i)
elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
elif sys.argv[i][:2] == "-h": HelpAndExit()
elif sys.argv[i][:1] == "-": Fatal("'%s' unknown argument" % sys.argv[i])
else: Fatal("'%s' unexpected" % sys.argv[i])
else: skip = 0
print("%d,%d,%s,%s" % (debug,verbose,infile,outfile))
Das Ziel von NextArg() besteht darin, das nächste Argument zurückzugeben, während nach fehlenden Daten gesucht wird, und „skip“ überspringt die Schleife, wenn NextArg() verwendet wird, sodass die Flag-Analyse auf einen Zeilen beschränkt bleibt.
Ich habe den Ansatz von Erco erweitert, um erforderliche Positionsargumente und optionale Argumente zu ermöglichen.Diese sollten vor -d, -v usw. stehen.Argumente.
Positions- und optionale Argumente können mit PosArg(i) bzw. OptArg(i, default) abgerufen werden.Wenn ein optionales Argument gefunden wird, wird die Startposition der Suche nach Optionen (z. B.-i) wird um 1 nach vorne verschoben, um einen „unerwarteten“ fatalen Fehler zu vermeiden.
import os,sys
def HelpAndExit():
print("<<your help output goes here>>")
sys.exit(1)
def Fatal(msg):
sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
sys.exit(1)
def NextArg(i):
'''Return the next command line argument (if there is one)'''
if ((i+1) >= len(sys.argv)):
Fatal("'%s' expected an argument" % sys.argv[i])
return(1, sys.argv[i+1])
def PosArg(i):
'''Return positional argument'''
if i >= len(sys.argv):
Fatal("'%s' expected an argument" % sys.argv[i])
return sys.argv[i]
def OptArg(i, default):
'''Return optional argument (if there is one)'''
if i >= len(sys.argv):
Fatal("'%s' expected an argument" % sys.argv[i])
if sys.argv[i][:1] != '-':
return True, sys.argv[i]
else:
return False, default
### MAIN
if __name__=='__main__':
verbose = 0
debug = 0
infile = "infile"
outfile = "outfile"
options_start = 3
# --- Parse two positional parameters ---
n1 = int(PosArg(1))
n2 = int(PosArg(2))
# --- Parse an optional parameters ---
present, a3 = OptArg(3,50)
n3 = int(a3)
options_start += int(present)
# --- Parse rest of command line ---
skip = 0
for i in range(options_start, len(sys.argv)):
if not skip:
if sys.argv[i][:2] == "-d": debug ^= 1
elif sys.argv[i][:2] == "-v": verbose ^= 1
elif sys.argv[i][:2] == "-i": (skip,infile) = NextArg(i)
elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
elif sys.argv[i][:2] == "-h": HelpAndExit()
elif sys.argv[i][:1] == "-": Fatal("'%s' unknown argument" % sys.argv[i])
else: Fatal("'%s' unexpected" % sys.argv[i])
else: skip = 0
print("Number 1 = %d" % n1)
print("Number 2 = %d" % n2)
print("Number 3 = %d" % n3)
print("Debug = %d" % debug)
print("verbose = %d" % verbose)
print("infile = %s" % infile)
print("outfile = %s" % outfile)