2010-11-05 11 views
24

Czy to możliwe, aby przynieść wiele wartości dla jednej opcji z użyciem getopt lub optparse, jak pokazano w poniższym przykładzie:Przetwarzanie wielu wartości dla jednej opcji za pomocą getopt/optparse?

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

Należy pamiętać, że liczba rzeczywistych wartości dla każdego wariantu (-c, -b) dało być albo 1 albo 100. Nie chcę używać: ./hello_world -c "arg1 arg2 arg3" -b "arg4 arg5 arg6 arg7"

Wydaje mi się, że to może nie być możliwe (i być może z naruszeniem POSIX), proszę poprawić mnie, jeśli się mylę.

Widziałem przykłady, w których wszystkie nie-opcje na końcu linii (./hello_world -c arg1 -b arg1 arg2 arg3) mogą być gromadzone ... ale nie dla pierwszej z wielu opcji.

Chciałbym, aby moja aplikacja działała na wielu platformach z różnymi wersjami Pythona, więc nie patrzyłem na argparser.

Odpowiedz

5

Możesz to zrobić za pomocą parametru nargs w argparse, który jest dostarczany z Python2.7, i do pobrania here.

Myślę, że jest to jeden z ulepszeń dodanych do argparse, który nie jest w optparse. Tak więc, niestety, nie wydaje mi się, że istnieje dobry sposób na obsłużenie tego przy pomocy optparse lub getopt (która jest jeszcze starsza).

Szybkim i brzydkim rozwiązaniem może być rezygnacja z optparse/getop/argparse i samodzielne przeanalizowanie wartości sys.argv.

Albo, idąc w przeciwnym kierunku, można rozważyć pakowania mrożonego kopię argparse (~ 88K) (przemianowany coś podobnego argparse_static) z programu i importowaniem to tak:

try: 
    import argparse 
except ImportError: 
    import argparse_static as argparse 

Że sposób, program będzie używał argparse, jeśli jest zainstalowany, i użyje argparse_static, jeśli tak nie jest. Co najważniejsze, nie musisz przepisywać wielu kodów, ponieważ argparse stanie się standardem.

+0

Dziękuję bardzo za dwie szybkie i dogłębne odpowiedzi. Udało mi się napisać własny parser opcji, który obsługuje rzeczy. Pobieranie statycznej kopii argparse nie było czymś, co rozważałem, ale teraz jest na stole. –

+0

Mój głos jest z argparse. Łatwiej jest mi się kręcić, niż optparse. (I byłem tym, który włamał się na optparse, gdy był to Optik, zanim wszedł do standardowej biblioteki) –

5

Ani getopt, ani optparse nie obsługują tego po wyjęciu z pudełka. Ponadto w trybie domyślnym (GNU) dodatkowe argumenty będą traktowane jako sparowane args, tj. Staną się dostępne jako pozostawione argumenty na końcu przetwarzania.

Konwencja byłoby wymagają wielokrotnego wymieniania samego argumentu tj

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

to będzie obsługiwany.

Jeśli naprawdę chcesz, aby działał w sposób określony przez ciebie (tzn. Zarówno -b, jak i -c, aż do następnego argumentu lub na końcu listy argumentów), możesz zhakować coś w oparciu o optparse. Odziedzicz z OptionParser i nadpisaj _process_short_opts. Jeśli jest to jedna z opcji, przetwórz ją w podklasie, a następnie przekaż do klasy bazowej.

+1

Dziękuję za wyjaśnienie konwencji. Tak właśnie myślałem ... Problem polega na tym, że liczba wartości dla każdej opcji sprawia, że ​​używanie tej konwencji nie jest przyjazne dla użytkownika. Myślę, że prawdopodobnie skończę iterować przez sys.argv i radzę sobie z tym sam. –

14

Tak, można tego dokonać przy pomocy optparse.

To jest przykład:

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

która drukuje:

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

Pełny przykład roboczych poniżej:

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

Więcej informacji na http://docs.python.org/library/optparse.html#adding-new-actions

4

Inną opcją byłoby zdefiniowanie separatora i przetworzenie go lokalnie, na przykład opcji w poleceniu mount.

Na przykład, jeśli , może być używany jako separator:

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

$ ./test -b opt1,opt2,opt3 

samo dla przestrzeni! Ale wtedy twoi użytkownicy muszą zacytować to poprawnie.

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

Przepraszam za spóźnienie na imprezę, ale właśnie to rozwiązałem optparse za pomocą flagi nargs.

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

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

Warto również zauważyć, że argparse (sugerowane przez unutbu) jest obecnie częścią standardowej dystrybucji Pythona podczas optparse jest przestarzała.

+0

Załóżmy, że jeśli chciałbym umieścić tutaj check, jeśli użytkownik nie ma argumentów wejściowych. jak odczytywać 4 argumenty w "option.Categories" jeden po drugim – kzs

+1

Zauważ, że ogranicza to do 4 argumentów dla '-c'. – Plasma

+0

Podążanie z @Plasma wymaga również 4 argumentów nie więcej i nie mniej. – sijpkes

8

Pomimo roszczeń innych komentarzy, jest to możliwe dzięki wanilowym optparse, przynajmniej w wersji python 2.7. Musisz tylko użyć action = "append". Od docs:

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

Jeśli -t3 jest widoczne w linii poleceń, optparse robi równowartość:

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

Jeżeli trochę później, --tracks = 4 jest widoczne, robi:

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

Jest to bardziej konwencjonalne rozwiązanie problemu. Przekazywanie wielu argumentów do pojedynczej flagi jest praktyką wprowadzającą w błąd i ma wysokie prawdopodobieństwo spowodowania problemów na linii. Jeśli potrzebujesz wyodrębnionych słów IFS przekazanych do * opcji *, zachęcam użytkowników do podania wartości, a następnie podzielenia jej na kod. W przeciwnym razie trzymaj się argumentów poleceń lub powtarzających się flag, jak zasugerowano w tej odpowiedzi. – Ben

3

łatwiejszy jeden:

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

Append action to najprostsze rozwiązanie tego problemu.

Powiązane problemy