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

StackOverflow https://stackoverflow.com/questions/20063

Вопрос

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

Это было полезно?

Решение

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

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

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 для объединения команд в цепочку.Это не то, что я могу легко объяснить в нескольких строках, но не стесняйтесь покопайтесь в моем репозитории для основного класса, а также класс, который его использует, и анализатор опций

Другие советы

В других ответах упоминается об этом argparse это подходящее решение для нового Python, но не приводите примеров использования.Для полноты приведем краткое описание того, как использовать 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

Использование докопта

С 2012 года Python имеет очень простой, мощный и действительно прохладный модуль для разбора аргументов называется документация.Он работает с Python 2.6–3.5 и не требует установки (просто скопируйте его).Вот пример, взятый из документации:

"""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 строки кода плюс строка документа, которая является Essential, и вы получите ваши аргументы, проанализированные и доступные в вашем объекте аргументов.Я же говорил тебе, что это круто, не так ли ;-)

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

С 2017 года Python-огонь есть еще один классный модуль, который может предоставить интерфейс 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

Круто, не так ли?

Новый модный способ argparse для эти причины.argparse > optparse > getopt

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

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

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

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.

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

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

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, вы пропускаете первый элемент, который является именем вашей программы, и анализируете только аргументы: sys.argv[1:]

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

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

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

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

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

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

Использовать 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?

На всякий случай, это может помочь, если вам нужно схватить Аргументы Юникода в 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()

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

Хотя 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

Аргумент «имя» фиксирует имя сценария и не используется.Вывод теста выглядит следующим образом:

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

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

Я думаю, что лучший способ для более крупных проектов — это 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

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

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

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

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''

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

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

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

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

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)

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

Цель здесь - быть кратким, каждый аргумент анализируется одной строкой, аргументы выстраиваются в ряд для удобства чтения, код прост и не зависит от каких-либо специальных модулей (только 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, включив в него необходимые позиционные аргументы и необязательные аргументы.Они должны предшествовать -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) 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top