2010-11-05 16 views
24

E 'possibile recuperare più valori per un'opzione utilizzando getopt o optparse, come mostrato nell'esempio seguente:Elaborazione di più valori per una singola opzione utilizzando getopt/optparse?

./hello_world -c arg1 arg2 arg3 -b arg4 arg5 arg6 arg7 

Si prega di notare che il numero dei valori effettivi per ogni opzione (-c, -b) potrebbe essere 1 o 100. Non voglio usare: ./hello_world -c "arg1 arg2 arg3" -b "arg4 arg5 arg6 arg7"

Mi sembra che questo potrebbe non essere possibile (e forse in violazione di POSIX), per favore correggimi se sbaglio.

Ho visto esempi in cui è possibile raccogliere tutte le non opzioni alla fine della riga (./hello_world -c arg1 -b arg1 arg2 arg3) ... ma non per la prima delle opzioni multiple.

Vorrei che la mia app lavorasse su una vasta gamma di piattaforme con diverse versioni di Python, quindi non ho guardato ad argparser.

risposta

5

È possibile eseguire questa operazione con il parametro nargs in argparse fornito con Python2.7 e scaricabile here.

Penso che sia uno dei miglioramenti aggiunti a argparse che non è in optparse. Quindi, sfortunatamente, non penso che ci sia un buon modo per gestirlo con optparse o getopt (che è ancora più vecchio).

Una soluzione rapida e sporca potrebbe essere quella di rinunciare allo optparse/getop/argparse e analizzare solo sys.argv.

Oppure, andando nella direzione opposta, si potrebbe considerare il confezionamento di una copia ghiacciato di argparse (~ 88K) (qualcosa rinominato come argparse_static) con il vostro programma, e importare in questo modo:

try: 
    import argparse 
except ImportError: 
    import argparse_static as argparse 

Quello modo, il programma utilizzerà argparse se è installato, e utilizzerà argparse_static se non lo è. Meglio di tutti, non sarà necessario riscrivere molto codice come argparse diventa standard.

+0

Grazie mille per due risposte rapide e approfondite. Sono riuscito a scrivere il mio parser opzionale che gestisce le cose. Scaricare una copia statica di argparse non era qualcosa che ho preso in considerazione, ma ora è sul tavolo. –

+0

Il mio voto è con argparse. È più facile capirlo che optparse. (E io ero uno che ha hackerato optparse quando era Optik, prima che fosse incluso nella libreria standard) –

5

Né getopt né optparse supportano questo fuori dalla scatola. Inoltre, nella modalità predefinita (GNU), gli argomenti aggiuntivi verrebbero trattati come argomenti intervallati, ovvero diventati disponibili come argomenti rimanenti alla fine dell'elaborazione.

La convenzione sarebbe richiedono ripetute menzionare dello stesso argomento, cioè

./hello_world -c arg1 -c arg2 -c arg3 -b arg4 -b arg5 -b arg6 -b arg7 

Questo è sarà supportato.

Se si vuole assolutamente farlo funzionare come si specifica (vale a dire, sia -b che -c si estendono fino al prossimo argomento o alla fine dell'elenco degli argomenti), quindi è possibile modificare qualcosa in base a optparse. Eredita da OptionParser e sovrascrive _process_short_opts. Se è una delle opzioni, elaborale nella sottoclasse, altrimenti inoltra alla classe base.

+1

Grazie per aver spiegato la convenzione. Questo era quello che pensavo .. Il problema è che il numero di valori per ogni opzione rende l'utilizzo di tale convenzione non user-friendly. Penso che probabilmente finirò per scorrere su sys.argv e gestire le cose da solo. –

14

Sì, può essere fatto con optparse.

Questo è un esempio:

./test.py --categories=aaa --categories=bbb --categories ccc arg1 arg2 arg3 

che stampa:

arguments: ['arg1', 'arg2', 'arg3'] 
options: {'categories': ['aaa', 'bbb', 'ccc']} 

completa esempio funzionante di seguito:

#!/usr/bin/env python 

import os, sys 
from optparse import OptionParser 
from optparse import Option, OptionValueError 

VERSION = '0.9.4' 

class MultipleOption(Option): 
    ACTIONS = Option.ACTIONS + ("extend",) 
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) 
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) 
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",) 

    def take_action(self, action, dest, opt, value, values, parser): 
     if action == "extend": 
      values.ensure_value(dest, []).append(value) 
     else: 
      Option.take_action(self, action, dest, opt, value, values, parser) 


def main(): 
    PROG = os.path.basename(os.path.splitext(__file__)[0]) 
    long_commands = ('categories') 
    short_commands = {'cat':'categories'} 
    description = """Just a test""" 
    parser = OptionParser(option_class=MultipleOption, 
          usage='usage: %prog [OPTIONS] COMMAND [BLOG_FILE]', 
          version='%s %s' % (PROG, VERSION), 
          description=description) 
    parser.add_option('-c', '--categories', 
         action="extend", type="string", 
         dest='categories', 
         metavar='CATEGORIES', 
         help='comma separated list of post categories') 

    if len(sys.argv) == 1: 
     parser.parse_args(['--help']) 

    OPTIONS, args = parser.parse_args() 
    print "arguments:", args 
    print "options:", OPTIONS 

if __name__ == '__main__': 
    main() 

Maggiori informazioni su http://docs.python.org/library/optparse.html#adding-new-actions

4

Un'altra opzione sarebbe quella di definire un separatore ed elaborarlo localmente, come le opzioni nel comando mount.

Per esempio, se , può essere utilizzato come un separatore:

... 
args, _ = getopt.getopt(sys.argv[1:],'b:') 
for flag, arg in args: 
    if flag=='-b': all_arguments = arg.split(',') 
... 

$ ./test -b opt1,opt2,opt3 

Lo stesso vale per lo spazio! Ma poi i tuoi utenti devono citarla correttamente.

$ ./test -b 'opt1 opt2 opt3' 
7

Mi dispiace venire in ritardo alla festa, ma ho appena risolto questo con optparse utilizzando la bandiera nargs.

parser.add_option('-c','--categories', dest='Categories', nargs=4) 

http://docs.python.org/2/library/optparse.html#optparse.Option.nargs

Si noti inoltre, che argparse (suggerito da unutbu) è ora parte della distribuzione pitone standard mentre optparse è obsoleto.

+0

Supponiamo che volessi mettere un controllo qui, se l'utente non ha inserito argomenti. come leggere 4 argomenti in "option.Categories" uno per uno – kzs

+1

Si noti che questo limita a 4 argomenti per '-c'. – Plasma

+0

In seguito a @Plasma richiede anche 4 argomenti né più né meno. – sijpkes

8

Nonostante le affermazioni degli altri commenti, questo è possibile con vanilla optparse, almeno a partire da python 2.7. Hai solo bisogno di usare action = "append". Dalla docs:

parser.add_option("-t", "--tracks", action="append", type="int") 

Se -t3 si vede sulla riga di comando, optparse esegue l'equivalente di:

options.tracks = [] 
options.tracks.append(int("3")) 

Se, un po 'più tardi, --tracks = 4 è visto, lo fa:

options.tracks.append(int("4")) 
+1

Questa è una soluzione più convenzionale al problema. Passare più argomenti a un singolo flag è una pratica ingannevole e ha un'alta probabilità di causare problemi lungo la linea. Se hai bisogno che le parole IFS separate siano passate a un'opzione *, incoraggerei gli utenti a citare il valore e poi dividerlo nel codice. Altrimenti, attenersi agli argomenti del comando o alle bandiere ripetute come suggerito in questa risposta. – Ben

3

uno più facile:

make_option(
    "-c", 
    "--city", 
    dest="cities", 
    action="append", 
    default=[], 
    help="specify cities", 
) 

Append action è la soluzione più semplice per questo problema.

Problemi correlati