Pergunta

Qual é o mais fácil, mais conciso, e a maioria flexível método ou biblioteca para analisar argumentos de linha de comando do Python?

Foi útil?

Solução

Esta resposta sugere optparse o que é apropriado para versões mais antigas do Python.Para Python 2.7 e superior, argparse substitui optparse.Ver esta resposta Para maiores informações.

Como outras pessoas apontaram, é melhor optar pelo optparse do que pelo getopt.getopt é praticamente um mapeamento um-para-um das funções padrão da biblioteca C getopt(3) e não é muito fácil de usar.

optparse, embora seja um pouco mais detalhado, é muito melhor estruturado e mais simples de estender posteriormente.

Aqui está uma linha típica para adicionar uma opção ao seu analisador:

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

Praticamente fala por si;no momento do processamento, ele aceitará -q ou --query como opções, armazenará o argumento em um atributo chamado query e terá um valor padrão se você não especificá-lo.Também é autodocumentado porque você declara o argumento de ajuda (que será usado quando executado com -h/--help) ali mesmo com a opção.

Normalmente você analisa seus argumentos com:

options, args = parser.parse_args()

Por padrão, isso analisará os argumentos padrão passados ​​​​para o script (sys.argv[1:])

options.query será então definido com o valor que você passou para o script.

Você cria um analisador simplesmente fazendo

parser = optparse.OptionParser()

Estes são todos os princípios básicos que você precisa.Aqui está um script Python completo que mostra isso:

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 linhas de python que mostram o básico.

Salve-o em sample.py e execute-o uma vez com

python sample.py

e uma vez com

python sample.py --query myquery

Além disso, você descobrirá que o optparse é muito fácil de estender.Em um de meus projetos, criei uma classe Command que permite aninhar facilmente subcomandos em uma árvore de comando.Ele usa optparse fortemente para encadear comandos.Não é algo que eu possa explicar facilmente em poucas linhas, mas fique à vontade para navegue no meu repositório para a classe principal, bem como uma classe que o utiliza e o analisador de opções

Outras dicas

Outras respostas mencionam que argparse é o caminho a seguir para o novo Python, mas não fornece exemplos de uso.Para completar, aqui está um breve resumo de como usar argparse:

1) Inicializar

import argparse

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

2) Adicione argumentos

# 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) Analisar

args = parser.parse_args()

4) Acesso

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

5) Verifique os valores

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

Uso

Uso correto:

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

Argument values:
1
2
3
True

Argumentos incorretos:

$ ./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

Ajuda completa:

$ ./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

Usando docopt

Desde 2012 Python tem uma ferramenta muito fácil, poderosa e realmente legal módulo para análise de argumentos chamado docopt.Funciona com Python 2.6 a 3.5 e não precisa de instalação (basta copiá-lo).Aqui está um exemplo retirado de sua documentação:

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

Então é isso:2 linhas de código mais sua string de documento que é essencial e você terá seus argumentos analisados ​​e disponíveis em seu objeto de argumentos.Eu te disse que é legal, não disse ;-)

Usando python-fire

Desde 2017 fogo python tem outro módulo legal que pode fornecer uma interface CLI ao seu código com você fazendo zero análise de argumentos.Aqui está um exemplo simples da documentação (este pequeno programa expõe a função double para a linha de comando):

import fire

class Calculator(object):

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

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

Na linha de comando, você pode executar:

> calculator.py double 10
20
> calculator.py double --number=15
30

Incrível, não é?

O novo jeito moderno é argparse para esses razões.argparse > optparse > getopt

atualizar: A partir de py2.7 argparse faz parte da biblioteca padrão e optar por analisar está obsoleto.

eu prefiro Clique.Ele abstrai as opções de gerenciamento e permite "(...) criar lindas interfaces de linha de comando de forma combinável com o mínimo de código necessário".

Aqui está um exemplo de uso:

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

Ele também gera automaticamente páginas de ajuda bem formatadas:

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

Quase todo mundo está usando obteropt

Aqui está o código de exemplo para o documento:

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

Então, em uma palavra, é assim que funciona.

Você tem dois tipos de opções.Aqueles que estão recebendo argumentos e aqueles que são como interruptores.

sys.argv é praticamente seu char** argv em C.Como em C você pula o primeiro elemento que é o nome do seu programa e analisa apenas os argumentos: sys.argv[1:]

Getopt.getopt irá analisá-lo de acordo com a regra que você fornecer no argumento.

"ho:v" aqui descreve os argumentos curtos: -ONELETTER.O : significa que -o aceita um argumento.

Finalmente ["help", "output="] descreve argumentos longos ( --MORETHANONELETTER ).O = após a saída mais uma vez significa que a saída aceita um argumento.

O resultado é uma lista de pares (opção, argumento)

Se uma opção não aceita nenhum argumento (como --help aqui o arg parte é uma string vazia.Geralmente, você deseja fazer um loop nesta lista e testar o nome da opção como no exemplo.

Espero que isso tenha ajudado você.

Usar optparse que vem com a biblioteca padrão.Por exemplo:

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

Fonte: Usando Python para criar ferramentas de linha de comando UNIX

No entanto, a partir do Python 2.7, o optparse está obsoleto, consulte: Por que usar argparse em vez de optparse?

Caso você precise, isso pode ajudar se você precisar pegar argumentos unicode no Win32 (2K, XP etc):


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

Padrões leves de argumentos de linha de comando

Embora argparse é ótimo e é a resposta certa para opções de linha de comando totalmente documentadas e recursos avançados, você pode usar padrões de argumentos de função para lidar com argumentos posicionais diretos de maneira muito simples.

import sys

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

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

O argumento 'name' captura o nome do script e não é usado.A saída do teste é semelhante a esta:

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

Para scripts simples onde desejo apenas alguns valores padrão, acho isso suficiente.Você também pode querer incluir algum tipo de coerção nos valores de retorno ou os valores da linha de comando serão todos strings.

Acho que a melhor maneira para projetos maiores é optparse, mas se você está procurando uma maneira fácil, talvez http://werkzeug.pocoo.org/documentation/script é algo para você.

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

Então, basicamente, todas as funções Action_* são expostas à linha de comando e uma boa mensagem de ajuda é gerada gratuitamente.

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

Eu prefiro optparse a getopt.É muito declarativo:você informa os nomes das opções e os efeitos que elas devem ter (por exemplo, definindo um campo booleano) e ele lhe devolve um dicionário preenchido de acordo com suas especificações.

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

consoleargs merece ser mencionado aqui.É muito fácil de usar.Confira:

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

Agora no console:

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

O código Argparse pode ser mais longo que o código de implementação real!

Esse é um problema que encontro nas opções de análise de argumentos mais populares: se seus parâmetros forem apenas modestos, o código para documentá-los se tornará desproporcionalmente grande em relação ao benefício que eles fornecem.

Um relativamente novo na cena de análise de argumentos (eu acho) é lugar.

Ele faz algumas compensações reconhecidas com o argparse, mas usa documentação in-line e envolve simplesmente main() tipo função função:

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)

Aqui está um método, não uma biblioteca, que parece funcionar para mim.

Os objetivos aqui são ser concisos, cada argumento analisado por uma única linha, os argumentos alinhados para facilitar a leitura, o código é simples e não depende de nenhum módulo especial (apenas os + sys), avisa sobre argumentos ausentes ou desconhecidos normalmente , use um loop for/range() simples e funcione em python 2.xe 3.x

São mostrados dois sinalizadores de alternância (-d, -v) e dois valores controlados por argumentos (-i xxx e -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))

O objetivo de NextArg() é retornar o próximo argumento enquanto verifica dados ausentes, e 'skip' pula o loop quando NextArg() é usado, mantendo a análise do sinalizador em um liner.

Estendi a abordagem de Erco para permitir argumentos posicionais obrigatórios e argumentos opcionais.Estes devem preceder o -d, -v etc.argumentos.

Argumentos posicionais e opcionais podem ser recuperados com PosArg(i) e OptArg(i, padrão) respectivamente.Quando um argumento opcional é encontrado, a posição inicial da pesquisa de opções (por exemplo-i) é movido 1 para frente para evitar causar um fatal 'inesperado'.

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) 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top