Який найкращий спосіб проаналізувати аргументи командного рядка? [зачинено]


251

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

Відповіді:


183

Ця відповідь підказує, optparseщо підходить для старих версій Python. Для Python 2.7 і вище, argparseзамінює 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 рядків пітона, які показують вам основи.

Збережіть його у sample.py та запустіть його один раз

python sample.py

і один раз с

python sample.py --query myquery

Крім того, ви побачите, що оптипар дуже просто розширити. В одному зі своїх проектів я створив клас Command, який дозволяє легко вкладати підкоманди в дерево команд. Він використовує optparse сильно для ланцюга команд разом. Це я не дуже легко поясню в декількох рядках, але не соромтеся переглядати в моєму сховищі основний клас, а також клас, який його використовує, і параметр-аналізатор


9
Ця відповідь дивовижно зрозуміла і легко прослідкувати - для python 2.3 th 2.6. Для python 2.7+ це не найкраща відповідь, оскільки аргпарсес тепер є частиною стандартної бібліотеки та застарілим optparse.
matt wilkie

У моєму випадку я хочу профілювати свою програму, щоб виявити повільність. Є ще один інструмент під назвою [tuna] ( 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
Якщо ви виявите аргументацію занадто багатослівною, використовуйте натомість 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 рядки коду плюс ваш doc рядок, що є важливим, і ви отримуєте ваші аргументи розібрані та доступні у вашому об'єкті аргументів.

Використання пітон-вогню

З 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

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 застарілою.


Ваше основне посилання - 404, тому я замінив його посиланням на питання SO, яке стосується тієї ж теми.
Джо Холлоуей

28

Я віддаю перевагу Клік . Він резюмує керування параметрами та дозволяє "(...) створювати красиві інтерфейси командного рядка компонованим способом з якомога меншим кодом".

Ось приклад використання:

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.

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** argvC. Як і в C, ви пропускаєте перший елемент, який є назвою вашої програми, і аналізуєте лише аргументи:sys.argv[1:]

Getopt.getopt буде аналізувати його відповідно до правила, яке ви наводите в аргументі.

"ho:v"Тут описані короткі аргументи: -ONELETTER. В :означає , що -oприймає один аргумент.

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

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

Якщо параметр не приймає жодного аргументу (наприклад, --helpтут),arg частина є порожнім рядком. Потім зазвичай потрібно зациклитися на цьому списку і протестувати ім'я параметра, як у прикладі.

Я сподіваюся, що це вам допомогло.


6
З припиненням роботи getoptв нових версіях Python ця відповідь застаріла.
shuttle87

1
@ shuttle87 Щодо python3.7.2, getoptце ще не застаріло ... Але в його документації зазначено, що він в основному надається користувачам, знайомим з getopt()функцією C , і визнає, що для інших користувачів argparseможе бути кращим рішенням, що дозволяє "написати менше коду та отримати краща довідка та повідомлення про помилки ".
Skippy le Grand Gourou

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?


6

На випадок, якщо вам це знадобиться, це може допомогти, якщо вам потрібно захопити аргументи unicode на 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()

Дякую. Цей скрипт допоміг мені розробити дійсно складне цитування, яке мені потрібно було зробити при передачі команд запуску в 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.
historystamp

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. У всякому разі, я великий шанувальник такого підходу.
krassowski

3

Код Argparse може бути довшим, ніж фактичний код реалізації!

Це проблема, яку я знаходжу в більшості популярних варіантів аналізу аргументів, полягає в тому, що якщо ваші параметри лише скромні, код для їх документування стає непропорційно великим для вигоди, яку вони надають.

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

Він робить визнані компроміси з 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)

Інформація: найменше використання plac (як показано в прикладі) призначене лише для Python 3.x, оскільки він використовує анотації функції 3.x.
барна

1

консоліарги заслуговують на згадку тут. Він дуже простий у використанні. Перевір:

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

Я використовував аналогічний підхід у декларативному аналізаторі , див. Виведення аргументів (набір тексту, docstrings, kwargs) у документах. Основні відмінності: python3, підказки щодо типу, встановлення через pip.
krassowski

1
Останній
Борис

0

Ось метод, а не бібліотека, яка, здається, працює для мене.

Цілі тут повинні бути короткими, кожен аргумент розбирається одним рядком, аргументи складаються на читабельність, код простий і не залежить від будь-яких спеціальних модулів (лише os + sys), витончено попереджає про відсутні або невідомі аргументи , використовуйте простий цикл для / 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 () - повернути наступний аргумент під час перевірки відсутніх даних, а "пропустити" пропускає цикл, коли NextArg () використовується, зберігаючи прапор розбору до одного вкладиша.


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) 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.