Jaki jest najlepszy sposób na analizę argumentów wiersza poleceń? [zamknięte]

zamknięte . To pytanie musi być bardziej skoncentrowane . Obecnie nie przyjmuje odpowiedzi.

chcesz poprawić to pytanie? Update the question so it edytując ten post.

Zamknięte 1 rok temu .

Popraw to pytanie

Jaki jest najprostszy, tersest i większość elastyczna metoda do parsowania argumentów linii poleceń Pythona?

Author: abccd, 2008-08-21

15 answers

Ta odpowiedź sugeruje optparse, która jest odpowiednia dla starszych wersji Pythona. Dla Pythona 2.7 i nowszych, argparse zastępuje optparse. Zobacz ta odpowiedź aby uzyskać więcej informacji.

Jak zauważyli inni, lepiej wybrać optparse zamiast getopt. getopt jest w zasadzie mapowaniem jeden do jednego standardowych funkcji biblioteki getopt(3) C i nie jest bardzo łatwy w użyciu.

Optparse, choć jest nieco bardziej wyrazisty, jest znacznie lepiej zorganizowany i prostszy do / align = "left" /

Oto typowy wiersz, aby dodać opcję do parsera:

parser.add_option('-q', '--query',
            action="store", dest="query",
            help="query string", default="spam")

To mówi samo za siebie; w czasie przetwarzania, przyjmuje jako opcje-q lub --query, przechowuje argument w atrybucie zwanym query i ma domyślną wartość, jeśli go nie podasz. Jest to również samodokumentujące się w tym, że deklarujesz argument help (który będzie używany podczas uruchamiania z-h/--help) właśnie tam z opcją.

Zwykle analizujesz swoje argumenty z:

options, args = parser.parse_args()

Domyślnie parsuje standardowe argumenty przekazywane do skryptu (sys.argv[1:])

Opcje.zapytanie zostanie następnie ustawione na wartość przekazaną do skryptu.

Tworzysz parser po prostu wykonując

parser = optparse.OptionParser()
To wszystko, czego potrzebujesz. Oto kompletny skrypt Pythona, który to pokazuje:
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 linii Pythona, które pokazują podstawy.

Save it in sample.py i uruchom go raz z

python sample.py

I raz z

python sample.py --query myquery

Poza tym przekonasz się, że optparse jest bardzo łatwy do rozszerzenia. W jednym z moich projektów stworzyłem klasę poleceń, która pozwala na łatwe zagnieżdżanie podpowiedzi w drzewie poleceń. Używa optparse mocno do łączenia poleceń. Nie jest to coś, co mogę łatwo wyjaśnić w kilku linijkach, ale zapraszam do przeglądania w moim repozytorium dla głównej klasy, a także klasy, która go używa i parsera opcji

 191
Author: Thomas Vander Stichele,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:47

argparse to jest droga. Oto krótkie podsumowanie, jak go używać:

1) Initialize

import argparse

# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')

2) Dodaj Argumenty

# 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) Parse

args = parser.parse_args()

4) Access

print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)

5) Wartości Kontrolne

if args.pos_arg > 10:
    parser.error("pos_arg cannot be larger than 10")

Użycie

Prawidłowe użycie:

$ ./app 1 2 --opt_arg 3 --switch

Argument values:
1
2
3
True

Błędne argumenty:

$ ./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

Pełna pomoc:

$ ./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
 254
Author: Andrzej Pronobis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-02 11:00:22

Używanie docopt

Od 2012 roku istnieje bardzo łatwy, potężny i naprawdę fajny moduł do parsowania argumentów o nazwie docopt. Oto przykład zaczerpnięty z jego dokumentacji:

"""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)

Więc to jest to: 2 linie kodu plus Twój ciąg doc, który jest niezbędny, a twoje argumenty są przetwarzane i dostępne w obiekcie arguments.

Używanie Pythona-fire

Od 2017 roku istnieje kolejny fajny moduł o nazwie python-fire. Może Wygeneruj interfejs CLI dla Twojego kodu, wykonując zero parsowanie argumentów. Oto prosty przykład z dokumentacji (ten mały program wyświetla funkcję double w wierszu poleceń):

import fire

class Calculator(object):

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

Z linii poleceń można uruchomić:

> calculator.py double 10
20
> calculator.py double --number=15
30
 79
Author: ndemou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-07 13:23:45

Nowy sposób hip jest argparse z tych Powodów. argparse > optparse > getopt

Update: od py2.7 argparse jest częścią biblioteki standardowej i optparse jest przestarzały.

 39
Author: Silfheed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-31 21:45:52

Wolę Kliknij . Zawiera opcje zarządzania i pozwala " (...) tworzenie pięknych interfejsów wiersza poleceń w sposób komponowalny z tak małą ilością kodu, jak to konieczne".

Oto przykład użycia:

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()

Automatycznie generuje ładnie sformatowane strony pomocy:

$ 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.
 31
Author: suda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-03 20:36:48

Prawie każdy używa getopt

Oto przykładowy kod doc:

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
Jednym słowem, oto jak to działa.

Masz dwa rodzaje opcji. Tych, którzy przyjmują argumenty, oraz tych, którzy są tak jak przełączniki.

sys.argv jest w zasadzie twoim char** argv W C. tak jak w C pomijasz pierwszy element, który jest nazwą Twojego programu i analizujesz tylko argumenty: sys.argv[1:]

Getopt.getopt przetworzy go zgodnie z regułą ty się kłócisz.

"ho:v" Oto krótkie argumenty: -ONELETTER. : oznacza, że -o przyjmuje jeden argument.

Wreszcie ["help", "output="] opisuje długie argumenty (--MORETHANONELETTER). = po wyjściu ponownie oznacza, że output akceptuje jeden argument.

Wynikiem jest lista par (opcja,argument)

Jeśli opcja nie akceptuje żadnego argumentu (jak --help tutaj), część arg jest pustym łańcuchem znaków. Następnie Zwykle chcesz zapętlić tę listę i przetestować nazwa opcji jak w przykładzie.

Mam nadzieję, że ci to pomogło.
 14
Author: fulmicoton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 17:36:53

Użyj optparse, który pochodzi z biblioteki standardowej. Na przykład:

#!/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()

Źródło: używanie Pythona do tworzenia uniksowych narzędzi wiersza poleceń

Jednak od Pythona 2.7 optparse jest przestarzały, zobacz: Dlaczego używać argparse zamiast optparse?

 14
Author: Corey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

Lightweight command line argument defaults

Chociaż argparse jest świetny i jest właściwą odpowiedzią dla w pełni udokumentowanych przełączników wiersza poleceń i zaawansowanych funkcji, możesz użyć domyślnych argumentów funkcji, aby obsłużyć proste argumenty pozycyjne w bardzo prosty sposób.

import sys

def get_args(name='default', first='a', second=2):
    return first, int(second)

first, second = get_args(*sys.argv)
print first, second

Argument 'name' przechwytuje nazwę skryptu i nie jest używany. Wynik testu wygląda tak:

> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20

Dla prostych skryptów, w których chcę tylko domyślnych wartości, uważam to za całkiem wystarczy. Możesz również dodać pewien rodzaj przymusu w zwracanych wartościach lub wartości wiersza poleceń będą ciągami znaków.

 8
Author: Simon Hibbs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-08 12:44:34

Na wszelki wypadek, może to pomóc, jeśli potrzebujesz pobrać argumenty unicode na Win32 (2K, XP itp.):


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()
 6
Author: Shadow2531,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 14:59:54

Kod Argparse może być dłuższy niż rzeczywisty kod implementacyjny!

To jest problem, który znalazłem z najbardziej popularnych opcji parsowania argumentów jest to, że jeśli parametry są tylko skromne, kod do ich udokumentowania staje się nieproporcjonalnie duży dla korzyści, które zapewniają.

Względną nowością do sceny parsowania argumentów (myślę) jest plac .

To sprawia, że niektóre uznane kompromisów z argparse, ale używa wbudowanej dokumentacji i owija się po prostu wokół main() type function function:

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)
 4
Author: QA Collective,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-05 07:17:58

Wolę optparse niż getopt. Jest to bardzo deklaratywne: podajesz mu nazwy opcji i efekty, które powinny mieć (np. ustawiając pole logiczne), a to oddaje Ci słownik wypełniony zgodnie z Twoimi specyfikacjami.

Http://docs.python.org/lib/module-optparse.html

 3
Author: Chris Conway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 15:22:20

Myślę, że najlepszym sposobem na większe projekty jest optparse, ale jeśli szukasz łatwego sposobu, Może http://werkzeug.pocoo.org/documentation/script to coś dla Ciebie.

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()

Więc w zasadzie każda funkcja action_* jest wystawiona na działanie linii poleceń i ładną wiadomość pomoc jest generowana za darmo.

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
 3
Author: Peter Hoffmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-27 19:41:22

Consoleargs Jest bardzo łatwy w użyciu. Zobacz też:

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()

Teraz w konsoli:

% 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''
 1
Author: stalk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-30 05:43:02

Oto metoda, a nie biblioteka, która wydaje się działać dla mnie.

Cele tutaj są zwięzłe, każdy argument parsowany pojedynczą linią, linia args dla czytelności, kod jest prosty i nie zależy od żadnych specjalnych modułów( tylko os + sys), ostrzega o brakujących lub nieznanych argumentów wdzięcznie, używa prostej pętli for / range() i działa w Pythonie 2.x i 3.x

Pokazane są dwa znaczniki przełączania (- d,- v) i dwie wartości kontrolowane przez argumenty (- i xxx I-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))

Celem nextarg() jest zwrócenie następnego argumentu podczas sprawdzania brakujących danych, a 'skip' pomija pętlę, gdy jest używana NextArg (), zachowując flagę parsującą do jednej linii.

 1
Author: erco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-09 17:43:18

Rozszerzyłem podejście Erco, aby umożliwić wymagane argumenty pozycyjne i argumenty opcjonalne. Powinny one poprzedzać-d, - v itd. argumenty.

Argumenty pozycyjne i opcjonalne można pobrać odpowiednio za pomocą PosArg(i) i OptArg(i, domyślnie). Po znalezieniu opcjonalnego argumentu pozycja początkowa wyszukiwania opcji (np.-i)jest przesuwana o 1 do przodu, aby uniknąć 'nieoczekiwanego' fatalnego wyniku.

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) 
 0
Author: Erik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 12:32:43