Qual è il modo migliore per analizzare gli argomenti della riga di comando? [chiuso]


251

Qual è il più semplice , tersest , e la maggior parte flessibile metodo o una libreria per il parsing argomenti della riga di comando Python?

Risposte:


183

Questa risposta suggerisce optparsequale sia appropriato per le versioni precedenti di Python. Per Python 2.7 e versioni successive, argparsesostituisce optparse. Vedi questa risposta per maggiori informazioni.

Come altri hanno sottolineato, è meglio andare con optparse su getopt. getopt è praticamente un mapping uno a uno delle funzioni standard della libreria C getopt (3) e non molto facile da usare.

optparse, pur essendo un po 'più prolisso, è molto meglio strutturato e più semplice da estendere in seguito.

Ecco una linea tipica per aggiungere un'opzione al tuo parser:

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

Parla praticamente da sé; al momento dell'elaborazione, accetterà -q o --query come opzioni, memorizzerà l'argomento in un attributo chiamato query e avrà un valore predefinito se non lo si specifica. È anche auto-documentante nel dichiarare l'argomento help (che verrà usato quando eseguito con -h / - help) proprio lì con l'opzione.

Di solito analizzi i tuoi argomenti con:

options, args = parser.parse_args()

Questo, per impostazione predefinita, analizzerà gli argomenti standard passati allo script (sys.argv [1:])

options.query verrà quindi impostato sul valore passato allo script.

Si crea un parser semplicemente facendo

parser = optparse.OptionParser()

Queste sono tutte le basi di cui hai bisogno. Ecco uno script Python completo che mostra questo:

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 linee di pitone che ti mostrano le basi.

Salvalo in sample.py ed eseguilo una volta con

python sample.py

e una volta con

python sample.py --query myquery

Oltre a ciò, scoprirai che optparse è molto facile da estendere. In uno dei miei progetti, ho creato una classe Command che consente di nidificare facilmente i sottocomandi in un albero dei comandi. Usa optparse pesantemente per concatenare i comandi insieme. Non è qualcosa che posso facilmente spiegare in poche righe, ma sentiti libero di navigare nel mio repository per la classe principale, così come una classe che la usa e il parser di opzioni


9
Questa risposta è meravigliosamente chiara e facile da seguire - per Python da 2.3 a 2.6. Per python 2.7+ non è la risposta migliore in quanto argparse fa ora parte della libreria standard e optparse è deprecato.
Matt Wilson

Nel mio caso, voglio profilare la mia applicazione per rilevare la lentezza. C'è un altro strumento chiamato [tonno] ( github.com/nschloe/tuna ) che mi permette di profilare l'intera applicazione semplicemente aggiungendo agrs -mcProfile -o program.profma agrparcer sta catturando questi argomenti, come posso passare questi argomenti a python exe ???
Yogeshwar,

231

argparseè la strada da percorrere. Ecco un breve riassunto di come usarlo:

1) Inizializza

import argparse

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

2) Aggiungi argomenti

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

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

5) Controllare i valori

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

uso

Uso corretto:

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

Argument values:
1
2
3
True

Argomenti errati:

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

Aiuto completo:

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

10
Questo è molto conciso e utile ed ecco il documento ufficiale (per comodità): docs.python.org/3/library/argparse.html
Christophe Roussy

1
Se trovi argparse troppo prolisso usa invece plac .
Nimitz14,

76

Utilizzando docopt

Dal 2012 v'è una molto semplice, potente e davvero fresco modulo per argomento l'analisi chiamato docopt . Ecco un esempio tratto dalla sua documentazione:

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

Quindi è così: 2 righe di codice più la tua stringa doc che è essenziale e ottieni gli argomenti analizzati e disponibili nell'oggetto argomenti.

Usando Python-Fire

Dal 2017 c'è un altro fantastico modulo chiamato python-fire . Può generare un'interfaccia CLI per il tuo codice eseguendo l' analisi dell'argomento zero . Ecco un semplice esempio dalla documentazione (questo piccolo programma espone la funzione doublealla riga di comando):

import fire

class Calculator(object):

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

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

Dalla riga di comando, puoi eseguire:

> calculator.py double 10
20
> calculator.py double --number=15
30

4
come fa docopt "non necessita di installazione"? è un modulo Python, quindi deve essere installato. 'ImportError: nessun modulo chiamato docopt'
acuto dal

1
@keen non è sicuramente incluso in Python ma non è necessario installarlo: "puoi semplicemente rilasciare il file docopt.py nel tuo progetto - è autonomo" - github.com/docopt/docopt
ndemou

9
abbiamo solo diverse definizioni di installazione e volevo segnalarlo per i futuri lettori.
appassionato

1
@keen Ho aggiunto una nota su "nessuna installazione" per le persone che condividono la tua definizione :-)
ndemou

39

Il nuovo modo alla moda è argparseper questi motivi. argparse> optparse> getopt

aggiornamento: a partire da py2.7 argparse fa parte della libreria standard e optparse è obsoleto.


Il tuo link principale è 404, quindi l'ho sostituito con un link a una domanda SO che affronta lo stesso argomento.
Joe Holloway,

28

Preferisco fare clic . Estrae le opzioni di gestione e permette "(...) di creare bellissime interfacce da riga di comando in modo compostabile con il minimo codice necessario".

Ecco un esempio di utilizzo:

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

Inoltre genera automaticamente pagine di aiuto ben formattate:

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

14

Praticamente tutti usano getopt

Ecco il codice di esempio per il documento:

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

Quindi, in una parola, ecco come funziona.

Hai due tipi di opzioni. Coloro che stanno ricevendo argomenti e quelli che sono come interruttori.

sys.argvè praticamente tuo char** argvin C. Come in C salti il ​​primo elemento che è il nome del tuo programma e analizzi solo gli argomenti:sys.argv[1:]

Getopt.getopt lo analizzerà secondo la regola che dai nell'argomento.

"ho:v"qui descrive le brevi argomenti: -ONELETTER. Il :mezzo che -oaccetta un argomento.

["help", "output="]Descrive infine argomenti lunghi ( --MORETHANONELETTER). L' =output after indica ancora una volta che l'output accetta un argomento.

Il risultato è un elenco di coppie (opzione, argomento)

Se un'opzione non accetta alcun argomento (come --helpqui) la argparte è una stringa vuota. Di solito, quindi, si desidera eseguire il ciclo su questo elenco e testare il nome dell'opzione come nell'esempio.

Spero che questo ti abbia aiutato.


6
Con la deprecazione di getoptnelle versioni più recenti di Python questa risposta non è più aggiornata.
shuttle87,

1
@ shuttle87 A partire da python3.7.2, non getoptè ancora deprecato ... Ma la sua documentazione afferma che è principalmente fornita agli utenti che hanno familiarità con la getopt()funzione C , e riconosce che per gli altri utenti argparsepotrebbe essere una soluzione migliore, che consente di "scrivere meno codice e ottenere migliore aiuto e messaggi di errore ".
Skippy le Grand Gourou

14

Utilizzare ciò optparseche viene fornito con la libreria standard. Per esempio:

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

Fonte: utilizzo di Python per creare strumenti da riga di comando UNIX

Tuttavia a partire da Python 2.7 optparse è obsoleto, vedi: Perché usare argparse anziché optparse?


6

Nel caso in cui potrebbe essere necessario, questo può aiutare se avete bisogno di afferrare gli argomenti Unicode su Win32 (2k, XP, ecc):


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

Grazie. Questo script mi ​​ha aiutato a elaborare alcune citazioni davvero complicate che dovevo fare quando passavo i comandi di avvio a GVim.
telotortium,

6

Valori predefiniti dell'argomento della riga di comando

Sebbene argparsesia eccezionale ed è la risposta giusta per opzioni della riga di comando completamente documentate e funzionalità avanzate, è possibile utilizzare le impostazioni predefinite degli argomenti delle funzioni per gestire gli argomenti posizionali in modo molto semplice.

import sys

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

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

L'argomento 'name' acquisisce il nome dello script e non viene utilizzato. L'output del test è simile al seguente:

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

Per gli script semplici in cui desidero solo alcuni valori predefiniti, lo trovo abbastanza sufficiente. Potresti anche voler includere un certo tipo di coercizione nei valori di ritorno o i valori della riga di comando saranno tutti stringhe.


2
le virgolette non corrispondono nella dichiarazione def.
historystamp


3

Penso che il modo migliore per progetti più grandi sia optparse, ma se stai cercando un modo semplice, forse http://werkzeug.pocoo.org/documentation/script è qualcosa per te.

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

Quindi praticamente ogni funzione action_ * è esposta alla riga di comando e un bel messaggio di aiuto viene generato gratuitamente.

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

Ho sviluppato un piccolo pacchetto che utilizza la creazione automatica di argomenti: declarative_parser. Naturalmente, se si sta lavorando con werkzeug, potrebbe essere meglio mantenere il werkzung.script. Ad ogni modo, sono un grande fan di tale approccio.
Krassowski,

3

Il codice Argparse può essere più lungo del codice di implementazione reale!

Questo è un problema che trovo con le opzioni di analisi degli argomenti più popolari è che se i tuoi parametri sono solo modesti, il codice per documentarli diventa sproporzionatamente grande a vantaggio che offrono.

Un nuovo arrivato relativamente all'argomento che analizza la scena (penso) è plac .

Effettua alcuni compromessi riconosciuti con argparse, ma utilizza la documentazione in linea e si avvolge semplicemente attorno alla main()funzione di tipo funzione:

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)

Punto di informazione: l'uso più accurato di plac (come mostrato nell'esempio) è solo per Python 3.x, perché utilizza le annotazioni della funzione 3.x.
barny

1

consoleargs merita di essere menzionato qui. È molto facile da usare Controlla:

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

Ora in console:

% 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''

Ho usato un approccio simile in dichiarative-parser , vedi la deduzione degli argomenti (typing, docstrings, kwargs) nei documenti. Differenze principali: python3, suggerimenti sul tipo, installabile tramite pip.
Krassowski,

1
Ultimo impegno nel 2012
Boris,

0

Ecco un metodo, non una libreria, che sembra funzionare per me.

Gli obiettivi qui sono di essere concisi, ogni argomento analizzato da una singola riga, gli arg lineini per la leggibilità, il codice è semplice e non dipende da alcun modulo speciale (solo os + sys), avverte con grazia di argomenti mancanti o sconosciuti , usa un semplice ciclo per / range () e funziona su python 2.xe 3.x

Sono mostrati due flag di commutazione (-d, -v) e due valori controllati da argomenti (-i xxx e -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))

L'obiettivo di NextArg () è quello di restituire l'argomento successivo durante la ricerca di dati mancanti e 'skip' salta il ciclo quando si utilizza NextArg (), mantenendo il flag analizzato fino a una riga.


0

Ho esteso l'approccio di Erco per consentire gli argomenti posizionali richiesti e gli argomenti opzionali. Questi dovrebbero precedere gli argomenti -d, -v ecc.

Argomenti posizionali e opzionali possono essere recuperati rispettivamente con PosArg (i) e OptArg (i, default). Quando viene trovato un argomento facoltativo, la posizione iniziale della ricerca di opzioni (ad es. -I) viene spostata 1 in avanti per evitare di causare un fatale "imprevisto".

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) 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.