2012-03-02 10 views
9

GNU getopt e strumenti da riga di comando che lo utilizzano, consentono l'interleazione di opzioni e argomenti, noti come opzioni di permutazione (vedere http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt). Anche il modulo Perl Getopt :: Long supporta questo (con qw (: config gnu_getopt)). argparse sembra non supportare (o nemmeno menzionare) le opzioni di permutazione.L'ordine di argomento permuto argparse di Python può essere simile a gnu getopt?

Ci sono molte domande SO relative all'argomento/opt order, ma nessuna sembra rispondere a questa domanda: Può essere fatto argparse per permutare l'ordine degli argomenti come getopt?

Il caso d'uso è una firma riga di comando prototipo come GNU sorta:

sort [opts] [files] 

in cui 1) le opzioni ei file sono permutati, e 2) l'elenco dei file può contenere zero o più argomenti.

Ad esempio:

import argparse 
p = argparse.ArgumentParser(); 
p.add_argument('files',nargs='*',default=['-']); 
p.add_argument('-z',action='store_true') 

p.parse_args(['-z','bar','foo']) # ok 
p.parse_args(['bar','foo','-z']) # ok 
p.parse_args(['bar','-z','foo']) # not okay 
usage: ipython [-h] [-z] [files [files ...]] 

ho provato:

  • p.parse_known_args - non si lamenta, ma in realtà non permuto sia e non esitano sugli argomenti sembrano opzioni non valide (ad es. --bogus o -b sopra).
  • p.add_argument ('files', nargs = argparse.REMAINDER) - opzione -z è incluso nel file a meno che prima args posizionali
  • p.add_argument ('files', nargs = '*', azione =' aggiungere');

Voglio implementare qualcosa vicino al prototipo di ordinamento GNU di cui sopra. Non mi interessa un flag che possa essere specificato per ogni file (ad es. -f file1 -f file2).

risposta

3

Non ho visto nulla di definitivo nella documentazione argparse affermando che può o non può permutare. In base alle tue osservazioni, in cui la permutazione non è riuscita e il seguente documento cita, ho intenzione di concludere che non può essere fatto.

  1. C'è già un modulo esplicitamente chiamato 'getopt':

    Nota Il modulo getopt è un parser per le opzioni della riga di comando la cui API è progettato per essere familiare agli utenti della funzione C getopt(). Gli utenti che non hanno familiarità con la funzione C getopt() o che desiderano scrivere codice meno codice e ottenere una migliore assistenza e messaggi di errore dovrebbero prendere in considerazione utilizzando il modulo argparse invece.

  2. Anche l'impostazione predefinita per getopt non permute, c'è un metodo più esplicitamente definito dal nome gnu_getopt():

    Questa funzione funziona come getopt(), tranne che la modalità di scansione stile GNU viene utilizzato per impostazione predefinita. Ciò significa che l'opzione e gli argomenti non opzionali possono essere combinati.

  3. Nei documenti getopt, il riferimento di cui sopra per argparse è anche esagerato con l'inserimento dei seguenti:

    nota che un CLI equivalente potrebbe essere prodotto con meno codice e più informativo aiuto e messaggi di errore utilizzando il modulo argparse:

Agai n, niente di definitivo, ma, a mio parere, viene tracciata una netta divisione tra getopt e argparse con la documentazione che favorisce/sostiene argparse.

Ecco un esempio utilizzando gnu_getop() che risponde con efficienza il test -z [file [file]]:

>>> args = 'file1 -z file2'.split() 
>>> args 
['file1', '-z', 'file2'] 
>>> opts, args = getopt.gnu_getopt(args, 'z') 
>>> opts 
[('-z', '')] 
>>> args 
['file1', 'file2'] 

Edit 1: Andare permute Yourself, con argparse

Ispirato alla definizione di "permute" nel 'Usando Getopt "pagina a cui sei collegato,

Il valore predefinito è di permutare il contenuto di argv durante la scansione quindi che alla fine tutte le non-opzioni sono alla fine.

che ne dici di permutare la stringa arg prima di passarla a parse_args()?

import argparse 

p = argparse.ArgumentParser(); 
p.add_argument('files',nargs='*',default=['-']); 
p.add_argument('-z',action='store_true') 

rotazione proprio:

import re 

def permute(s, opts_ptn='-[abc]'): 
    """Returns a permuted form of arg string s using a regular expression.""" 
    opts = re.findall(opts_ptn, s) 
    args = re.sub(opts_ptn, '', s) 
    return '{} {}'.format(' '.join(opts), args).strip() 

>>> p.parse_args(permute('bar -z foo', '-[z]').split()) 
Namespace(files=['bar', 'foo'], z=True) 

Sfruttando getopt:

import getopt 

def permute(s, opts_ptn='abc'): 
    """Returns a permuted form of arg string s using `gnu_getop()'.""" 
    opts, args = getopt.gnu_getopt(s.split(), opts_ptn) 
    opts = ' '.join([''.join(x) for x in opts]) 
    args = ' '.join(args) 
    return '{} {}'.format(opts, args).strip() 

>>> p.parse_args(permute('bar -z foo', 'z').split()) 
Namespace(files=['bar', 'foo'], z=True) 
4

Ecco una soluzione rapida che decodifica la lista degli argomenti uno (opzioni, argomenti posizionali) coppia alla volta.

import argparse 

class ExtendAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     items = getattr(namespace, self.dest, None) 
     if items is None: 
      items = [] 
     items.extend(values) 
     setattr(namespace, self.dest, items) 

parser = argparse.ArgumentParser() 
parser.add_argument('files', nargs='*', action=ExtendAction) 
parser.add_argument('-z', action='store_true') 
parser.add_argument('-v', action='count') 
parser.add_argument('args_tail', nargs=argparse.REMAINDER) 

def interleaved_parse(argv=None): 
    opts = parser.parse_args(argv) 
    optargs = opts.args_tail 
    while optargs: 
     opts = parser.parse_args(optargs, opts) 
     optargs = opts.args_tail 
    return opts 

print(interleaved_parse('-z bar foo'.split())) 
print(interleaved_parse('bar foo -z'.split())) 
print(interleaved_parse('bar -z foo'.split())) 
print(interleaved_parse('-v a -zv b -z c -vz d -v'.split())) 

uscita:

Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True) 
Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True) 
Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True) 
Namespace(args_tail=[], files=['a', 'b', 'c', 'd'], v=4, z=True) 

Nota: Non tentare di usare questo con altri argomenti non di bandiera (oltre ad una singola nargs='*' argomento e l'argomento args_tail). Il parser non conoscerà le chiamate precedenti di parse_args, quindi memorizzerà il valore errato per questi argomenti non di flag. Per ovviare al problema, è possibile analizzare manualmente l'argomento nargs='*' dopo aver utilizzato interleaved_parse.

Problemi correlati