Обрабатывать несколько значений для одной опции с помощью getopt/optparse?

Можно ли получить несколько значений для одной опции, используя getopt или optparse, как показано в примере ниже:

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

Обратите внимание, что число фактических значений для каждого параметра (-c, -b) может быть 1 или 100. Я не хочу использовать:./hello_world -c "arg1 arg2 arg3" -b "arg4 arg5 arg6 arg7"

Мне кажется, что это может быть невозможно (и, возможно, в нарушение POSIX), пожалуйста, исправьте меня, если я ошибаюсь.

Я видел примеры, где все не-опции в конце строки (./hello_world -c arg1 -b arg1 arg2 arg3) можно собрать... но не для первого из нескольких вариантов.

Мне бы хотелось, чтобы мое приложение работало на широком спектре платформ с разными версиями Python, поэтому я не смотрел на argparser.

7 ответов

Да, это можно сделать с помощью optparse.

Это пример:

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

который печатает:

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

Полный рабочий пример ниже:

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

Более подробная информация на http://docs.python.org/library/optparse.html

Несмотря на утверждения других комментариев, это возможно с vanilla optparse, по крайней мере, в Python 2.7. Вам просто нужно использовать action="append". Из документов:

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

Если -t3 виден в командной строке, optparse делает эквивалент:

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

Если чуть позже, --tracks=4 видно, это делает:

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

Извините, что опоздал на вечеринку, но я решил это с optparse, используя флаг nargs.

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

http://docs.python.org/2/library/optparse.html

Стоит также отметить, что argparse (предложенный unutbu) теперь является частью стандартного дистрибутива python, тогда как optparse устарел.

Другой вариант - определить разделитель и обработать его локально, как параметры в команде mount.

Например, если , может быть использован в качестве разделителя:

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

$ ./test -b opt1,opt2,opt3

То же самое для космоса! Но тогда ваши пользователи должны процитировать это правильно.

$ ./test -b 'opt1 opt2 opt3'

Ни getopt, ни optparse не поддерживают это из коробки. Кроме того, в режиме по умолчанию (GNU) дополнительные аргументы будут обрабатываться как перемежающиеся аргументы, то есть станут доступны как оставшиеся аргументы в конце обработки.

Конвенция будет требовать повторного упоминания одного и того же аргумента, т.е.

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

Это будет поддерживаться.

Если вы абсолютно хотите, чтобы это работало так, как вы указываете (т.е. оба параметра -b и -c расширяются до следующего аргумента или до конца списка аргументов), вы можете взломать что-то вместе на основе optparse. Наследовать от OptionParser и переопределять _process_short_opts. Если это один из ваших вариантов, обработайте его в подклассе, иначе перешлите в базовый класс.

Вы можете сделать это с nargs параметр в argparse который поставляется с Python2.7, и загружается здесь.

Я думаю, что это одно из улучшений, добавленных в argparse которого нет в optparse, Так что, к сожалению, я не думаю, что есть хороший способ справиться с этим optparse или же getopt (который еще старше).

Быстрое и грязное решение может быть отказаться optparse/getop/argparse и просто разобрать sys.argv сам.

Или, двигаясь в противоположном направлении, вы можете подумать об упаковке замороженной копии argparse (~88K) (переименованной в нечто вроде argparse_static) с вашей программой и импортируя ее так:

try:
    import argparse
except ImportError:
    import argparse_static as argparse

Таким образом, программа будет использовать argparse если он установлен и будет использовать argparse_static если это не так. Лучше всего, вам не придется переписывать много кода, как argparse становится стандартным.

Проще:

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

Действие добавления - самое простое решение этой проблемы.

Другие вопросы по тегам