Каков наилучший способ разбора аргументов командной строки? [закрыто]

251

Какой самый простой , краткий и самый гибкий метод или библиотека для анализа аргументов командной строки Python?

kamens
источник

Ответы:

183

Этот ответ предполагает, optparseчто подходит для более старых версий Python. Для Python 2.7 и выше argparseзаменяет optparse. Смотрите этот ответ для получения дополнительной информации.

Как отмечали другие люди, вам лучше использовать optparse вместо getopt. getopt в значительной степени является взаимно-однозначным отображением стандартных функций библиотеки getopt (3) C и не очень прост в использовании.

optparse, хотя и немного более многословный, гораздо лучше структурирован и его проще расширять в дальнейшем.

Вот типичная строка для добавления опции в ваш парсер:

parser.add_option('-q', '--query',
            action="store", dest="query",
            help="query string", default="spam")

Это в значительной степени говорит само за себя; во время обработки он принимает -q или --query в качестве параметров, сохраняет аргумент в атрибуте с именем query и имеет значение по умолчанию, если вы его не указали. Это также является самодокументированным, поскольку вы объявляете аргумент справки (который будет использоваться при запуске с -h / - help) прямо с опцией.

Обычно вы анализируете свои аргументы с помощью:

options, args = parser.parse_args()

По умолчанию это будет анализировать стандартные аргументы, передаваемые скрипту (sys.argv [1:])

Затем параметру options.query будет присвоено значение, которое вы передали в сценарий.

Вы создаете парсер просто

parser = optparse.OptionParser()

Это все основы, которые вам нужны. Вот полный скрипт Python, который показывает это:

import optparse

parser = optparse.OptionParser()

parser.add_option('-q', '--query',
    action="store", dest="query",
    help="query string", default="spam")

options, args = parser.parse_args()

print 'Query string:', options.query

5 строк Python, которые показывают основы.

Сохраните его в sample.py и запустите один раз с

python sample.py

и однажды с

python sample.py --query myquery

Кроме того, вы обнаружите, что optparse очень легко расширить. В одном из моих проектов я создал класс Command, который позволяет вам легко вкладывать подкоманды в дерево команд. Он использует optparse для объединения команд. Это не то, что я могу легко объяснить в нескольких строках, но не стесняйтесь просматривать в моем хранилище основной класс, а также класс, который его использует, и анализатор параметров

Томас Вандер Стичеле
источник
9
Этот ответ удивительно ясен и прост для понимания - для python 2.3 - 2.6. Для python 2.7+ это не лучший ответ, так как argparse теперь является частью стандартной библиотеки и optparse устарел.
Мэтт Уилки
В моем случае я хочу профилировать свое приложение, чтобы обнаружить медлительность. Есть еще один инструмент под названием [тунец] ( github.com/nschloe/tuna ), который позволяет мне профилировать все приложение, просто добавляя agrs, -mcProfile -o program.profно agrparcer захватывает эти аргументы, как передать эти аргументы в python exe ???
Йогешвар
231

argparseэто путь Вот краткое описание того, как его использовать:

1) Инициализировать

import argparse

# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')

2) Добавить аргументы

# Required positional argument
parser.add_argument('pos_arg', type=int,
                    help='A required integer positional argument')

# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
                    help='An optional integer positional argument')

# Optional argument
parser.add_argument('--opt_arg', type=int,
                    help='An optional integer argument')

# Switch
parser.add_argument('--switch', action='store_true',
                    help='A boolean switch')

3) Разбор

args = parser.parse_args()

4) Доступ

print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)

5) Проверьте значения

if args.pos_arg > 10:
    parser.error("pos_arg cannot be larger than 10")

использование

Правильное использование:

$ ./app 1 2 --opt_arg 3 --switch

Argument values:
1
2
3
True

Неверные аргументы:

$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'

$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10

Полная помощь:

$ ./app -h

usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]

Optional app description

positional arguments:
  pos_arg            A required integer positional argument
  opt_pos_arg        An optional integer positional argument

optional arguments:
  -h, --help         show this help message and exit
  --opt_arg OPT_ARG  An optional integer argument
  --switch           A boolean switch
Анджей Пронобис
источник
10
Это очень кратко и полезно, и вот официальный документ (для удобства): docs.python.org/3/library/argparse.html
Кристоф Руссси
1
Если вы находите argparse слишком многословным, используйте вместо этого plac .
Nimitz14
76

Используя docopt

С 2012 года появился очень простой, мощный и действительно классный модуль для разбора аргументов под названием docopt . Вот пример, взятый из его документации:

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)

Итак, вот оно: 2 строки кода плюс ваша строка документа, что очень важно, и ваши аргументы анализируются и становятся доступными в вашем объекте аргументов.

Использование Python-Fire

С 2017 года появился еще один классный модуль под названием python-fire . Он может генерировать интерфейс CLI для вашего кода, когда вы выполняете разбор нулевого аргумента. Вот простой пример из документации (эта небольшая программа предоставляет функцию doubleв командной строке):

import fire

class Calculator(object):

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

Из командной строки вы можете запустить:

> calculator.py double 10
20
> calculator.py double --number=15
30
ndemou
источник
4
Как Docopt "не требует установки"? Это модуль Python, поэтому он должен быть установлен. 'ImportError: нет модуля с именем docopt'
горячо
1
@keen он точно не включен в python, но вам не нужно его устанавливать: «вы можете просто вставить файл docopt.py в ваш проект - он самодостаточен» - github.com/docopt/docopt
ndemou
9
у нас просто есть разные определения установки - и я хотел бы отметить это для будущих читателей.
Сильное
1
@keen Я добавил заметку на тему «нет установки» для людей, разделяющих ваше определение :-)
ndemou
39

Новый модный путь argparseпо этим причинам. argparse> optparse> getopt

обновление: с py2.7 argparse является частью стандартной библиотеки, а optparse устарела.

Silfheed
источник
Ваша главная ссылка - 404, поэтому я заменил ее ссылкой на вопрос SO, посвященный той же теме.
Джо Холлоуэй
28

Я предпочитаю Click . Он абстрагирует параметры управления и позволяет «(...) создавать красивые интерфейсы командной строки с возможностью компоновки с минимальным количеством кода».

Вот пример использования:

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()

Он также автоматически генерирует красиво отформатированные страницы справки:

$ python hello.py --help
Usage: hello.py [OPTIONS]

  Simple program that greets NAME for a total of COUNT times.

Options:
  --count INTEGER  Number of greetings.
  --name TEXT      The person to greet.
  --help           Show this message and exit.
Suda
источник
14

Практически все используют getopt

Вот пример кода для документа:

import getopt, sys

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    output = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-o", "--output"):
            output = a

Одним словом, вот как это работает.

У вас есть два типа вариантов. Те, кто получает аргументы, и те, кто просто как переключатели.

sys.argvв значительной степени ваш char** argvв C. Как и в C вы пропустите первый элемент, который является именем вашей программы и анализирует только аргументы:sys.argv[1:]

Getopt.getopt проанализирует его в соответствии с правилом, которое вы даете в аргументе.

"ho:v"Здесь описаны краткие аргументы: -ONELETTER. В :означает , что -oпринимает один аргумент.

Наконец ["help", "output="]описывает длинные аргументы ( --MORETHANONELETTER). =После вывода еще раз означает , что выход принимает один аргументы.

Результатом является список пары (опция, аргумент)

Если опция не принимает никакого аргумента (как --helpздесь), argчасть является пустой строкой. Затем вы обычно хотите зациклить этот список и проверить имя опции, как в примере.

Я надеюсь, что это помогло вам.

fulmicoton
источник
6
С появлением устаревших getoptверсий Python этот ответ устарел.
shuttle87
1
@ shuttle87 Начиная с python3.7.2, getoptвсе еще не считается устаревшим… Но в его документации говорится, что он в основном предназначен для пользователей, знакомых с getopt()функцией C , и признает, что для других пользователей argparseможет быть лучшим решением, позволяющим «писать меньше кода и получать Лучшая помощь и сообщения об ошибках ".
Скиппи ле Гран Гуру
14

Используйте optparseкоторый поставляется со стандартной библиотекой. Например:

#!/usr/bin/env python
import optparse

def main():
  p = optparse.OptionParser()
  p.add_option('--person', '-p', default="world")
  options, arguments = p.parse_args()
  print 'Hello %s' % options.person

if __name__ == '__main__':
  main()

Источник: Использование Python для создания инструментов командной строки UNIX

Однако, начиная с Python 2.7, optparse устарела, см .: Зачем использовать argparse, а не optparse?

Corey
источник
6

Только в случае , если вы , возможно , потребуется, это может помочь , если вам нужно захватить аргументы Юникода Win32 (2K, XP и т.д.):


from ctypes import *

def wmain(argc, argv):
    print argc
    for i in argv:
        print i
    return 0

def startup():
    size = c_int()
    ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
    ref = c_wchar_p * size.value
    raw = ref.from_address(ptr)
    args = [arg for arg in raw]
    windll.kernel32.LocalFree(ptr)
    exit(wmain(len(args), args))
startup()
Shadow2531
источник
Спасибо. Этот скрипт помог мне выработать действительно сложное цитирование, которое мне нужно было сделать при передаче команд запуска в GVim.
телоториум
6

Облегченный аргумент командной строки по умолчанию

Хотя argparseэто замечательно и является правильным ответом для полностью документированных переключателей командной строки и расширенных функций, вы можете использовать значения по умолчанию для аргументов функции, чтобы очень просто обрабатывать прямые позиционные аргументы.

import sys

def get_args(name='default', first='a', second=2):
    return first, int(second)

first, second = get_args(*sys.argv)
print first, second

Аргумент 'name' фиксирует имя скрипта и не используется. Тестовый вывод выглядит так:

> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20

Для простых скриптов, где я просто хочу некоторые значения по умолчанию, я нахожу это вполне достаточным. Возможно, вы также захотите включить приведение типа в возвращаемые значения, иначе значения командной строки будут строками.

Саймон Хиббс
источник
2
кавычки не совпадают в операторе def.
историческая отметка
3

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

http://docs.python.org/lib/module-optparse.html

Крис Конвей
источник
3

Я думаю, что лучший способ для более крупных проектов - это optparse, но если вы ищете легкий путь, возможно, http://werkzeug.pocoo.org/documentation/script вам подойдет.

from werkzeug import script

# actions go here
def action_foo(name=""):
    """action foo does foo"""
    pass

def action_bar(id=0, title="default title"):
    """action bar does bar"""
    pass

if __name__ == '__main__':
    script.run()

Таким образом, в основном каждая функция action_ * доступна для командной строки, и хорошее вспомогательное сообщение генерируется бесплатно.

python foo.py 
usage: foo.py <action> [<options>]
       foo.py --help

actions:
  bar:
    action bar does bar

    --id                          integer   0
    --title                       string    default title

  foo:
    action foo does foo

    --name                        string
Питер Хоффманн
источник
Я разработал небольшой пакет , использующий автоматическое создание аргументов: declarative_parser. Конечно, если вы работаете с werkzeug, лучше сохранить werkzung.script. Во всяком случае, я большой поклонник такого подхода.
Крассовски
3

Argparse-код может быть длиннее, чем фактический код реализации!

Вот проблема, которую я нахожу с большинством популярных вариантов разбора аргументов, заключается в том, что если ваши параметры являются лишь скромными, то код для их документирования становится непропорционально большим, что приносит им пользу.

Относительно новичок в сцене разбора аргументов (я думаю) - это plac .

Он делает некоторые признанные компромиссы с argparse, но использует встроенную документацию и просто оборачивает main()функцию типа type:

def main(excel_file_path: "Path to input training file.",
     excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
     existing_model_path: "Path to an existing model to refine."=None,
     batch_size_start: "The smallest size of any minibatch."=10.,
     batch_size_stop:  "The largest size of any minibatch."=250.,
     batch_size_step:  "The step for increase in minibatch size."=1.002,
     batch_test_steps: "Flag.  If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."

    pass # Implementation code goes here!

if __name__ == '__main__':
    import plac; plac.call(main)
Коллектив QA
источник
Важное замечание: самое полезное использование plac (как показано в примере) предназначено только для Python 3.x, поскольку в нем используются аннотации функций 3.x.
Барни
1

consoleargs заслуживает упоминания здесь. Он очень прост в использовании. Проверьте это:

from consoleargs import command

@command
def main(url, name=None):
  """
  :param url: Remote URL 
  :param name: File name
  """
  print """Downloading url '%r' into file '%r'""" % (url, name)

if __name__ == '__main__':
  main()

Теперь в консоли:

% python demo.py --help
Usage: demo.py URL [OPTIONS]

URL:    Remote URL 

Options:
    --name -n   File name

% python demo.py http://www.google.com/
Downloading url ''http://www.google.com/'' into file 'None'

% python demo.py http://www.google.com/ --name=index.html
Downloading url ''http://www.google.com/'' into file ''index.html''
стебелек
источник
Я использовал аналогичный подход в декларативном парсере , см. Вывод аргументов (типизация, строки документации, kwargs) в документации. Основные отличия: python3, подсказки типа, возможность установки pip.
Крассовски
1
Последний коммит в 2012 году
Борис
0

Вот метод, а не библиотека, которая, кажется, работает для меня.

Цели здесь должны быть краткими, каждый аргумент анализируется одной строкой, аргументы выстраиваются для удобства чтения, код прост и не зависит от каких-либо специальных модулей (только os + sys), изящно предупреждает об отсутствующих или неизвестных аргументах , используйте простой цикл for / range () и работает через python 2.x и 3.x

Показаны два переключателя (-d, -v) и два значения, управляемых аргументами (-i xxx и -o xxx).

import os,sys

def HelpAndExit():
    print("<<your help output goes here>>")
    sys.exit(1)

def Fatal(msg):
    sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
    sys.exit(1)

def NextArg(i):
    '''Return the next command line argument (if there is one)'''
    if ((i+1) >= len(sys.argv)):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return(1, sys.argv[i+1])

### MAIN
if __name__=='__main__':

    verbose = 0
    debug   = 0
    infile  = "infile"
    outfile = "outfile"

    # Parse command line
    skip = 0
    for i in range(1, len(sys.argv)):
        if not skip:
            if   sys.argv[i][:2] == "-d": debug ^= 1
            elif sys.argv[i][:2] == "-v": verbose ^= 1
            elif sys.argv[i][:2] == "-i": (skip,infile)  = NextArg(i)
            elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
            elif sys.argv[i][:2] == "-h": HelpAndExit()
            elif sys.argv[i][:1] == "-":  Fatal("'%s' unknown argument" % sys.argv[i])
            else:                         Fatal("'%s' unexpected" % sys.argv[i])
        else: skip = 0

    print("%d,%d,%s,%s" % (debug,verbose,infile,outfile))

Целью NextArg () является возвращение следующего аргумента при проверке на отсутствие данных, а «skip» пропускает цикл при использовании NextArg (), сохраняя при этом флаг, разбирающий до одной строки.

ERCO
источник
0

Я расширил подход Эрко, чтобы учесть необходимые позиционные аргументы и необязательные аргументы. Они должны предшествовать аргументам -d, -v и т. Д.

Позиционные и необязательные аргументы могут быть получены с помощью PosArg (i) и OptArg (i, по умолчанию) соответственно. При обнаружении необязательного аргумента начальная позиция поиска параметров (например, -i) перемещается на 1 вперед, чтобы избежать «неожиданного» фатального исхода.

import os,sys


def HelpAndExit():
    print("<<your help output goes here>>")
    sys.exit(1)

def Fatal(msg):
    sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
    sys.exit(1)

def NextArg(i):
    '''Return the next command line argument (if there is one)'''
    if ((i+1) >= len(sys.argv)):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return(1, sys.argv[i+1])

def PosArg(i):
    '''Return positional argument'''
    if i >= len(sys.argv):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return sys.argv[i]

def OptArg(i, default):
    '''Return optional argument (if there is one)'''
    if i >= len(sys.argv):
        Fatal("'%s' expected an argument" % sys.argv[i])
    if sys.argv[i][:1] != '-':
        return True, sys.argv[i]
    else:
        return False, default


### MAIN
if __name__=='__main__':

    verbose = 0
    debug   = 0
    infile  = "infile"
    outfile = "outfile"
    options_start = 3

    # --- Parse two positional parameters ---
    n1 = int(PosArg(1))
    n2 = int(PosArg(2))

    # --- Parse an optional parameters ---
    present, a3 = OptArg(3,50)
    n3 = int(a3)
    options_start += int(present)

    # --- Parse rest of command line ---
    skip = 0
    for i in range(options_start, len(sys.argv)):
        if not skip:
            if   sys.argv[i][:2] == "-d": debug ^= 1
            elif sys.argv[i][:2] == "-v": verbose ^= 1
            elif sys.argv[i][:2] == "-i": (skip,infile)  = NextArg(i)
            elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
            elif sys.argv[i][:2] == "-h": HelpAndExit()
            elif sys.argv[i][:1] == "-":  Fatal("'%s' unknown argument" % sys.argv[i])
            else:                         Fatal("'%s' unexpected" % sys.argv[i])
        else: skip = 0

    print("Number 1 = %d" % n1)
    print("Number 2 = %d" % n2)
    print("Number 3 = %d" % n3)
    print("Debug    = %d" % debug)
    print("verbose  = %d" % verbose)
    print("infile   = %s" % infile)
    print("outfile  = %s" % outfile) 
Erik
источник