Domanda

Qual è il più semplice, conciso, e la maggior parte flessibile metodo o libreria per l'analisi degli argomenti della riga di comando Python?

È stato utile?

Soluzione

Questa risposta suggerisce optparse che è appropriato per le versioni precedenti di Python.Per Python 2.7 e versioni successive, argparse sostituisce optparse.Vedere questa risposta per maggiori informazioni.

Come hanno sottolineato altre persone, è meglio scegliere optparse anziché getopt.getopt è praticamente una mappatura uno a uno delle funzioni standard della libreria C getopt(3) e non è molto facile da usare.

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

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

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

Praticamente parla da solo;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 specifichi.È anche autodocumentante nel senso che dichiari l'argomento help (che verrà utilizzato 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.

Crei un parser semplicemente facendo

parser = optparse.OptionParser()

Queste sono tutte le nozioni di base 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 ti consente di annidare facilmente i sottocomandi in un albero dei comandi.Utilizza pesantemente optparse per concatenare i comandi.Non è qualcosa che posso spiegare facilmente in poche righe, ma sentitevi liberi di farlo curiosare nel mio repository per la classe principale, nonché una classe che lo utilizza e l'opzione parser

Altri suggerimenti

Altre risposte lo menzionano argparse è la strada da percorrere per il nuovo Python, ma non fornire esempi di utilizzo.Per completezza, ecco un breve riassunto di come utilizzare argparse:

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

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

Utilizzo

Uso corretto:

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

Argument values:
1
2
3
True

Argomentazioni errate:

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

Utilizzando Docopt

Dal 2012 Python ha un sistema molto semplice, potente e davvero Freddo modulo per l'analisi degli argomenti chiamato docopt.Funziona con Python dalla versione 2.6 alla 3.5 e non necessita di installazione (basta copiarlo).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 è questo:2 righe di codice più la stringa del documento which È essenziale e ottieni i tuoi argomenti analizzati e disponibili nel tuo oggetto argomenti.Te l'avevo detto che è bello, vero ;-)

Usando Python-Fire

Dal 2017 pitone-fuoco ha un altro modulo interessante che può fornire un'interfaccia CLI al tuo codice mentre lo fai zero analisi degli argomenti.Ecco un semplice esempio tratto dalla documentazione (questo piccolo programma espone function double alla 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 è possibile eseguire:

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

Fantastico, vero?

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

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

preferisco Clic.Astrae le opzioni di gestione e consente "(...) di creare bellissime interfacce a riga di comando in modo componibile 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.

Praticamente tutti lo 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 ricevono argomenti e quelli che sono proprio come switch.

sys.argv è praticamente tuo char** argv in 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à in base alla regola fornita in argomento.

"ho:v" qui vengono descritti i brevi argomenti: -ONELETTER.IL : significa che -o accetta un argomento.

Finalmente ["help", "output="] descrive argomenti lunghi ( --MORETHANONELETTER ).IL = dopo l'output significa 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 --help qui) il arg part è una stringa vuota.Quindi di solito vuoi eseguire il loop su questo elenco e testare il nome dell'opzione come nell'esempio.

Spero che questo ti abbia aiutato.

Utilizzo optparse che 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 è deprecato, vedere: Perché usare argparse anziché optparse?

Nel caso in cui ne avessi bisogno, questo potrebbe aiutarti se necessario preda 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()

Impostazioni predefinite degli argomenti leggeri della riga di comando

Sebbene argparse è fantastico ed è la risposta giusta per opzioni della riga di comando completamente documentate e funzionalità avanzate, puoi utilizzare i valori predefiniti degli argomenti delle funzioni per gestire argomenti posizionali semplici 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' cattura 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 script semplici in cui desidero solo alcuni valori predefiniti, lo trovo abbastanza sufficiente.Potresti anche voler includere qualche coercizione del tipo nei valori restituiti altrimenti i valori della riga di comando saranno tutti stringhe.

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 fondamentalmente ogni funzione Action_* è esposta alla riga di comando e viene generato un messaggio di buon aiuto 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

Preferisco optparse a getopt.È molto dichiarativo:gli dici i nomi delle opzioni e gli effetti che dovrebbero avere (ad esempio, impostando un campo booleano) e ti restituisce un dizionario popolato secondo le tue specifiche.

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

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

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

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 rispetto al vantaggio che forniscono.

Un relativamente nuovo arrivato nella scena di analisi degli argomenti (credo) è posto.

Fa alcuni compromessi riconosciuti con argparse, ma utilizza la documentazione in linea e si avvolge semplicemente main() tipo funzione 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)

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

Gli obiettivi qui devono essere concisi, ogni argomento analizzato da una singola riga, gli argomenti allineati per la leggibilità, il codice è semplice e non dipende da moduli speciali (solo os + sys), avverte con garbo su argomenti mancanti o sconosciuti , usa un semplice ciclo for/range() e funziona su Python 2.xe 3.x

Vengono mostrati due flag di attivazione/disattivazione (-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() è restituire l'argomento successivo durante il controllo dei dati mancanti e 'skip' salta il ciclo quando viene utilizzato NextArg(), mantenendo il flag analizzato su una riga.

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

Gli argomenti posizionali e facoltativi possono essere recuperati rispettivamente con PosArg(i) e OptArg(i, default).Quando viene trovato un argomento facoltativo, la posizione iniziale della ricerca delle opzioni (ad es.-i) viene spostato di 1 avanti per evitare di causare un fatale 'inaspettato'.

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) 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top