2012-03-02 11 views
9

GNU getopt i narzędzia wiersza poleceń, które go używają, pozwalają na przeplatanie opcji i argumentów, znanych jako opcje permutacji (patrz http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt). Moduł Getopt :: Long firmy Perl również to obsługuje (z qw (: config gnu_getopt)). argparse wydaje się nie obsługiwać (lub nawet wspominać) opcji permutacji.Czy argumencie Pythona można permutować kolejność argumentów jak gnu getopt?

Istnieje wiele pytań dotyczących SO związanych z poleceniem arg/opt, ale żadna nie wydaje się odpowiedzieć na to pytanie: czy można argumentować, aby permutować kolejność argumentów, np. Getopt?

Sprawa stosowanie jest prototypowy linii poleceń podpis jak GNU sort:

sort [opts] [files] 

w którym 1) Opcje i pliki są przesuwane, a 2) wykaz plik może zawierać zero lub więcej argumentów.

Na przykład:

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

próbowałem:

  • p.parse_known_args - nie narzekam, ale faktycznie nie permutacji albo i nie Balk o argumentach które wyglądają jak nieprawidłowe opcje (np. --bogus lub -b powyżej).
  • p.add_argument ('Pliki', nargs = argparse.REMAINDER) - opcja -z jest zawarty w plikach chyba przed pozycyjnych args
  • p.add_argument ('Files', nargs = '*', akcja =” dodać');

Chcę zaimplementować coś zbliżonego do prototypowego sortowania GNU powyżej. Nie jestem zainteresowany flagą, którą można określić dla każdego pliku (np. -f plik1 -f plik2).

Odpowiedz

3

Nie widziałem nic ostatecznego w dokumentacji argparse stwierdzającej, że może lub nie może się przeciągnąć. Na podstawie twoich własnych obserwacji, gdzie permutacja się nie udała, a następnie cytuje doc, stwierdzam, że nie da się tego zrobić.

  1. Istnieje już moduł wyraźnie nazwany „getopt”:

    Uwaga Moduł getopt jest parser dla opcji wiersza poleceń, których API ma być znane użytkownikom funkcji C getopt(). Użytkownicy , którzy nie są zaznajomieni z funkcją C getopt() lub którzy chcieliby napisać mniej kodu i uzyskać lepszą pomoc i komunikaty o błędach powinni rozważyć używając zamiast tego modułu argparse.

  2. Nawet domyślny dla getopt nie permutacji, jest bardziej jednoznacznie zdefiniowane metoda nazwana gnu_getopt():

    Funkcja ta działa podobnie getopt(), z wyjątkiem tego trybu skanowania stylu GNU jest używany domyślnie. Oznacza to, że można mieszać opcjonalne i nieopcjonalne argumenty.

  3. W GetOpt Dokumentach powyżej odniesienie do argparse jest dalej przesadzona przez włączenie następuje:

    Należy zauważyć, że równoważne linia poleceń mogą być wytwarzane z mniej kodu i bardziej czytelne Pomoc i komunikatów o błędach za pomocą modułu argparse:

Agai n, nic ostatecznego, ale dla mnie bardzo ostra jest różnica między getopt i argparse z dokumentacją faworyzującą/argumentującą argparse.

Oto przykład przy użyciu gnu_getop() który satifies 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: Przejdź przestawianie Yourself, z argparse

Zainspirowany definicji "permute" w „Używanie Getopt "strona, do której użytkownik podłączył się,

Ustawieniem domyślnym jest permutowanie zawartości argv podczas skanowania więc , że ostatecznie wszystkie nie-opcje są na końcu.

Co powiesz na permutowanie łańcucha arg przed przekazaniem go do parse_args()?

import argparse 

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

toczenia własne:

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) 

Wykorzystując 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

Oto szybkie rozwiązanie, które dekoduje listę argumentów jeden (opcje, pozycyjnych argumentów) parę na raz.

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

wyjściowa:

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) 

Uwaga: Nie należy używać tego z innych argumentów non-flag (poza jednym nargs='*' argumentu i args_tail argument). Analizator składni nie będzie wiedział o poprzednich wywołaniach parse_args, więc zapisze błędną wartość dla tych argumentów niezawierających flagi. W celu obejścia tego problemu można ręcznie przeanalizować argument nargs='*' po użyciu interleaved_parse.

Powiązane problemy