Jak czytać / przetwarzać argumenty wiersza poleceń?

623

Jestem pierwotnie programistą C. Widziałem wiele sztuczek i „hacków”, aby przeczytać wiele różnych argumentów.

W jaki sposób programiści Python mogą to zrobić?

Związane z

martineau
źródło
5
Użyj docopt (patrz odpowiedź @ ralbatross na stackoverflow.com/a/14790373/116891 ). Próbowałem wszystkich innych sposobów i naprawdę, docopt jest jedynym, którego użyję w przyszłości.
Pat
2
Nie sądzę, że jest jeden najlepszy sposób. argparse jest standardowe i funkcjonalne. docopt jest bardzo elegancki, ale nie ma go w standardowej bibliotece. Dla bardzo łatwego i lekkiego użycia możesz ustawić domyślne wartości funkcji, które będą obsługiwać domyślne argumenty linii poleceń .
Simon Hibbs,

Odpowiedzi:

456

Kanoniczne rozwiązanie w standardowej bibliotece to argparse( dokumenty ):

Oto przykład:

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
                    help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
                    action="store_false", dest="verbose", default=True,
                    help="don't print status messages to stdout")

args = parser.parse_args()

argparse obsługuje (między innymi):

  • Wiele opcji w dowolnej kolejności.
  • Krótkie i długie opcje.
  • Wartości domyślne
  • Generowanie komunikatu pomocy użytkowania.
Ayman Hourieh
źródło
27
Tak, są najlepsze. Ponieważ są częścią standardowej biblioteki, możesz być pewien, że będą one dostępne i łatwe w użyciu. w szczególności optparse jest potężny i łatwy.
Barry Wark,
4
optparse jest jednym z najlepszych; getopt jest stary i naprawdę należy go uważać za przestarzały.
jemfinch
12
w tym momencie (12/2011) argparse jest teraz uważane za lepszą opcję niż optparse, prawda?
oob
54
Dokumentacja Pythona sugeruje użycie argparse zamiast optparse.
earthmeLon
7
Ponieważ optparsejest przestarzały, pytający nie jest już członkiem w przypadku przepełnienia stosu, i jest to akceptowana odpowiedź na bardzo widoczne pytanie - rozważ całkowite przepisanie przykładowego kodu, aby argparsezamiast tego użyć stdlib .
wim
548
import sys

print("\n".join(sys.argv))

sys.argv to lista zawierająca wszystkie argumenty przekazane do skryptu w wierszu poleceń.

Gruntownie,

import sys
print(sys.argv[1:])
John Slavick
źródło
83
W przypadku naprawdę prostych rzeczy jest to odpowiedni sposób, chociaż prawdopodobnie chcesz tylko użyć sys.argv[1:](unika się nazwy skryptu).
Xiong Chiamiov
128

Chodzę ewangelizując dla argparse, co jest lepsze z tych powodów .. zasadniczo:

(skopiowane z linku)

  • moduł argparse może obsługiwać argumenty pozycyjne i opcjonalne, podczas gdy optparse może obsługiwać tylko argumenty opcjonalne

  • Argparse nie jest dogmatyczny, jak powinien wyglądać interfejs wiersza poleceń - obsługiwane są opcje takie jak -file lub / file, podobnie jak wymagane opcje. Optparse odmawia obsługi tych funkcji, woląc czystość od praktyczności

  • argparse produkuje bardziej pouczające komunikaty o użyciu, w tym użycie wiersza polecenia określone na podstawie argumentów, oraz komunikaty pomocy dla argumentów pozycyjnych i opcjonalnych. Moduł optparse wymaga napisania własnego ciągu użycia i nie ma możliwości wyświetlenia pomocy dla argumentów pozycyjnych.

  • argparse obsługuje działania, które zużywają zmienną liczbę argumentów wiersza poleceń, podczas gdy optparse wymaga wcześniejszej znajomości dokładnej liczby argumentów (np. 1, 2 lub 3)

  • argparse obsługuje parsery, które wysyłają do komend podrzędnych, podczas gdy optparse wymaga allow_interspersed_argsręcznego ustawienia i wykonania parsera

I mój osobisty faworyt:

  • argparse pozwala na określenie parametrów typu i akcji add_argument() za pomocą prostych wywołań, podczas gdy optparse wymaga hakowania atrybutów klasy takich jak STORE_ACTIONSlub, CHECK_METHODSaby uzyskać poprawne sprawdzenie argumentów
Silfheed
źródło
27
Jest to teraz część standardowego Pythona od wersji 2.7 i 3.2 :)
jpswain
Co to są „opcjonalne argumenty”? Mówisz, że są w optparse. Myślałem, że są to argumenty, które można podać lub nie, ale powiedziałeś, że są w optparse, mówiąc dalej, że „optparse wymaga, aby z góry była znana dokładna liczba argumentów”. Więc albo twoja definicja „opcjonalnego argumentu” różni się od tego, co myślałem, albo twoja odpowiedź jest niespójna z samą sobą.
ArtOfWarfare
1
Tylko kłopot: dokumentacja argparse jest również niesamowicie, niezwykle skomplikowana. Nie można uzyskać prostej odpowiedzi na pytanie: „jak sprawić, aby argument wiersza poleceń przyjmował pojedynczą wartość i jak uzyskać dostęp do tej wartości”. </gripe>
osman
2
@osman Ten delikatny samouczek dotyczący argparse może pomóc ...
lifebalance
2
@ArtOfWarfare „opcjonalne argumenty” w tym kontekście prawdopodobnie oznaczają argumenty określone za pomocą argumentów podobnych do opcji, takich jak -flub --foo, natomiast „dokładna liczba argumentów powinna być znana z góry” prawdopodobnie oznacza argumenty pozycyjne podane bez żadnych flag opcji.
mtraceur
67

Istnieje również argparsemoduł stdlib („ulepszenie” optparsemodułu stdlib ). Przykład ze wstępu do argparse :

# script.py
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'integers', metavar='int', type=int, choices=range(10),
         nargs='+', help='an integer in the range 0..9')
    parser.add_argument(
        '--sum', dest='accumulate', action='store_const', const=sum,
        default=max, help='sum the integers (default: find the max)')

    args = parser.parse_args()
    print(args.accumulate(args.integers))

Stosowanie:

$ script.py 1 2 3 4
4

$ script.py --sum 1 2 3 4
10
jfs
źródło
1
to tylko kopia i wklej
blitu12345
3
@ blitu12345 w momencie publikacji mojej odpowiedzi nie było innych odpowiedzi, które wspominałyby w jakikolwiek sposób o argparse. Sam moduł nie był w stdlib¶ Co masz przeciwko przykładowym kodom z dokumentacji? Dlaczego uważasz, że konieczne jest wymyślenie własnych przykładów zamiast przykładów dostarczonych przez autora modułu? I nie lubię odpowiedzi tylko z linkami (nie jestem sam).
jfs
1
Ludzie, którzy tu przyjechali, już mieli pojęcie, co jest w dokumentacji i będą tu tylko w celu dalszego wyjaśnienia tego tematu. Tak samo było z moją sprawą, ale tak naprawdę znalazłem tutaj kopię i wklej z oryginalnych dokumentów.
blitu12345
2
„Ludzie, którzy tu przychodzą, mieli już pojęcie z dokumentacji” - bardzo wątpię w to założenie. jakoś.
sjas,
49

Jednym ze sposobów na to jest użycie sys.argv. Spowoduje to wydrukowanie nazwy skryptu jako pierwszego argumentu i wszystkich innych przekazanych mu parametrów.

import sys

for arg in sys.argv:
    print arg
JPCosta
źródło
49

Docopt biblioteka jest bardzo śliski. Tworzy argument argumentu na podstawie ciągu użycia aplikacji.

Np. Z pliku readopt docopt:

"""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)
ralbatross
źródło
4
To szybko stało się moją ulubioną drogą. To parsowanie ciągów, więc jest trochę kruche, ale jest kruche w jednym miejscu i możesz podejrzeć swoją logikę na try.docopt.org . Opcjonalne i wykluczające się argumenty są wykonywane w naprawdę elegancki sposób.
gvoysey
4
Jestem zdesperowany, aby zobaczyć resztę kodu dla naval_fate.py
John Lawrence Aspden
48

Jeśli potrzebujesz czegoś szybkiego i niezbyt elastycznego

main.py:

import sys

first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)

Następnie uruchomić python main.py James Smith

w celu uzyskania następującego wyniku:

Cześć James Smith

Kent Munthe Caspersen
źródło
Bardziej realistyczne byłoby wykorzystanie python main.py "James Smith"co stawia James Smithw sys.argv[1]i wytwarza IndexErrorpodczas próby korzystania z nieistniejących sys.argv[2]. Cytowanie będzie w pewnym stopniu zależeć od platformy i powłoki, z której uruchamiasz Python.
tripleee
10
Nie zgadzam się, że moje użycie jest mniej realistyczne. Udajesz, że Twój program musi znać dokładne imię i nazwisko osoby, aby uruchomić skrypt w firmie, w której ludzie mogą mieć wiele imion i nazwisk? Jeśli James Smith ma Josepha jako dodatkowe imię lub nazwisko, to jak rozróżnić, czy Joseph jest dodatkowym imieniem czy nazwiskiem, jeśli tylko tak robisz python main.py "James Joseph Smith"? Jeśli masz problem z indeksem poza granicami, możesz dodać sprawdzanie liczby podanych argumentów. Mniej realistyczne czy nie, mój przykład pokazuje, jak radzić sobie z wieloma argumentami.
Kent Munthe Caspersen
1
Wszystkie pozostałe odpowiedzi dotyczą planowania lądowania na Księżycu. Po prostu używam gmail-trash-msg.py MessageID. Ta odpowiedź jest prosta, aby przekazać MessageIDparametr testowy sys.argv[1].
WinEunuuchs2Unix
26
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]
whi
źródło
19

Sam używam optparse, ale naprawdę podoba mi się kierunek, w którym podąża Simon Willison ze swoją niedawno wprowadzoną biblioteką optfunc . Działa przez:

„introspekcja definicji funkcji (w tym jej argumentów i ich wartości domyślnych) i wykorzystanie jej do skonstruowania analizatora składni argumentów wiersza poleceń.”

Na przykład ta definicja funkcji:

def geocode(s, api_key='', geocoder='google', list_geocoders=False):

zamienia się w ten tekst pomocy optparse:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER
Van Gale
źródło
8

Lubię getopt z stdlib, np .:

try:
    opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err: 
    usage(err)

for opt, arg in opts:
    if opt in ('-h', '--help'): 
        usage()

if len(args) != 1:
    usage("specify thing...")

Ostatnio pakuję coś podobnego do tego, aby rzeczy były mniej gadatliwe (np. Ukrywanie „-h”).

Peter Ericson
źródło
8

Pocoo za kliknięcie jest bardziej intuicyjna, wymaga mniej szablonowe, a jest co najmniej tak mocny jak argparse.

Jedyną słabością, jaką do tej pory napotkałem, jest to, że nie można dokonać wielu dostosowań stron pomocy, ale zwykle nie jest to wymagane, a docopt wydaje się być oczywistym wyborem.

Ryne Everett
źródło
7

Jak widać optparse „Moduł optparse jest przestarzały i nie będzie dalej rozwijany; rozwój będzie kontynuowany z modułem argparse ”.

tverrbjelke
źródło
5
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                   help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                   const=sum, default=max,
                   help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h

Ref-link: https://docs.python.org/3.3/library/argparse.html
JON
źródło
4

Być może interesuje Cię mały moduł Pythona, który napisałem, aby jeszcze bardziej ułatwić obsługę argumentów wiersza poleceń (open source i swobodny w użyciu) - Commando

Mufasa
źródło
1
Istnieje już inny moduł analizujący z wiersza poleceń o nazwie Commando: github.com/lakshmivyas/commando . Owija argparse za pomocą dekoratorów.
Roberto Bonvallet
1
ponowne wynalezienie pytona i koła
Derek
4

Polecam spojrzenie na docopt jako prostą alternatywę dla tych innych.

docopt to nowy projekt, który działa, analizując komunikat o użytkowaniu --help, zamiast wymagać od Ciebie implementacji wszystkiego samodzielnie. Musisz tylko umieścić komunikat o użyciu w formacie POSIX.

David C. Bishop
źródło
4

Jeszcze inną opcją jest argh . Opiera się na argparse i pozwala pisać takie rzeczy jak:

import argh

# declaring:

def echo(text):
    "Returns given word as is."
    return text

def greet(name, greeting='Hello'):
    "Greets the user with given name. The greeting is customizable."
    return greeting + ', ' + name

# assembling:

parser = argh.ArghParser()
parser.add_commands([echo, greet])

# dispatching:

if __name__ == '__main__':
    parser.dispatch()

Automatycznie wygeneruje pomoc i tak dalej, a można użyć dekoratorów, aby zapewnić dodatkowe wskazówki na temat tego, jak powinna działać analiza arg.

okrągłe ruiny
źródło
To najlepsze rozwiązanie. Używanie arghjest łatwiejsze niż inne biblioteki lib lub używanie sys.
Juanjo Salvador
Chciałem polubić, arghale nie jest to szczególnie odpowiednie w scenariuszach, w których twoim największym pragnieniem jest nie mieć polecenia z podkomendami.
tripleee
1
@tripleee YMMV, ale stwierdziłem, że była to raczej wada w dokumentacji niż w samej bibliotece. Wydaje się całkowicie wykonalne def frobnicate_spleches(...)zdefiniowanie funkcji, która robi wszystko, co robi skrypt, a następnie zrobienie tego if __name__ == '__main__': argh.dispatch_command(frobnicate_spleches)na końcu pliku.
okrągłe ruiny
0

Moje rozwiązanie to entrypoint2 . Przykład:

from entrypoint2 import entrypoint
@entrypoint
def add(file, quiet=True): 
    ''' This function writes report.

    :param file: write report to FILE
    :param quiet: don't print status messages to stdout
    '''
    print file,quiet

Tekst pomocy:

usage: report.py [-h] [-q] [--debug] file

This function writes report.

positional arguments:
  file         write report to FILE

optional arguments:
  -h, --help   show this help message and exit
  -q, --quiet  don't print status messages to stdout
  --debug      set logging level to DEBUG
ponty
źródło