Como ler / processar argumentos de linha de comando?

623

Eu sou originalmente um programador C. Eu já vi vários truques e "hacks" para ler muitos argumentos diferentes.

Quais são algumas das maneiras pelas quais os programadores de Python podem fazer isso?

Relacionado

Martineau
fonte
5
Use docopt (consulte a resposta de @ ralbatross em stackoverflow.com/a/14790373/116891 ). Eu tentei de qualquer outra maneira e, realmente, o docopt é o único que vou usar daqui para frente.
Pat
2
Eu não acho que há uma única maneira melhor. argparse é padrão e abrangente. O docopt é muito elegante, mas não está na biblioteca padrão. Para um uso leve e muito fácil, você pode fazer com que os valores padrão das funções tratem os padrões do argumento da linha de comandos para você .
Simon Hibbs

Respostas:

456

A solução canônica na biblioteca padrão é argparse( docs ):

Aqui está um exemplo:

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
                    help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
                    action="store_false", dest="verbose", default=True,
                    help="don't print status messages to stdout")

args = parser.parse_args()

argparse suporta (entre outras coisas):

  • Várias opções em qualquer ordem.
  • Opções curtas e longas.
  • Valores padrão.
  • Geração de uma mensagem de ajuda de uso.
Ayman Hourieh
fonte
27
Sim, estes são os melhores. Como eles fazem parte da biblioteca padrão, você pode ter certeza de que eles estarão disponíveis e são fáceis de usar. optparse, em particular, é poderoso e fácil.
Barry Wark
4
optparse é um dos melhores; getopt é antigo e realmente deve ser considerado obsoleto.
jemfinch
12
Neste ponto (12/2011), o argparse agora é considerado uma opção melhor do que o optparse, correto?
oob
54
A documentação do Python sugere o uso de argparse em vez de optparse.
earthmeLon
7
Como optparseestá obsoleto, o solicitante da pergunta não é mais um membro no estouro de pilha e esta é a resposta aceita em uma pergunta altamente visível - considere reescrever completamente seu código de exemplo para usar o stdlib argparse.
Wim
548
import sys

print("\n".join(sys.argv))

sys.argv é uma lista que contém todos os argumentos passados ​​para o script na linha de comando.

Basicamente,

import sys
print(sys.argv[1:])
John Slavick
fonte
83
Para coisas realmente simples, este é o caminho a seguir, embora você provavelmente só queira usar sys.argv[1:](evita o nome do script).
Xiong Chiamiov
128

Apenas por aí evangelizando por argparse, o que é melhor para esses motivos .. essencialmente:

(copiado do link)

  • O módulo argparse pode manipular argumentos posicionais e opcionais, enquanto o optparse pode manipular apenas argumentos opcionais

  • argparse não é dogmático sobre como deve ser sua interface de linha de comando - opções como -file ou / file são suportadas, assim como as opções necessárias. A Optparse se recusa a oferecer suporte a esses recursos, preferindo pureza a praticidade

  • argparse produz mensagens de uso mais informativas, incluindo o uso da linha de comando determinado a partir de seus argumentos, e ajuda mensagens para argumentos posicionais e opcionais. O módulo optparse exige que você escreva sua própria cadeia de uso e não tem como exibir ajuda para argumentos posicionais.

  • argparse suporta ações que consomem um número variável de argumentos da linha de comando, enquanto o optparse exige que o número exato de argumentos (por exemplo, 1, 2 ou 3) seja conhecido antecipadamente

  • O argparse suporta analisadores que despacham para subcomandos, enquanto o optparse requer a configuração allow_interspersed_argse a execução do despacho do analisador manualmente

E meu favorito pessoal:

  • argparse permite que os parâmetros de tipo e ação add_argument() sejam especificados com chamadas simples, enquanto o optparse requer atributos de classe de hackers como STORE_ACTIONSou CHECK_METHODSpara obter uma verificação de argumento adequada
Silfheed
fonte
27
Isto é agora parte do Python padrão a partir de 2.7 e 3.2 :)
jpswain
O que são "argumentos opcionais"? Você diz que eles estão em optparse. Eu pensei que eram argumentos que podem ou não ser fornecidos, mas você disse que eles estão em optparse enquanto afirmam que "optparse exige que o número exato de argumentos seja conhecido com antecedência". Portanto, sua definição de "argumento opcional" difere do que eu pensava ou sua resposta é inconsistente consigo mesma.
ArtOfWarfare
1
Apenas uma queixa: a documentação argparse também é insanamente, insanamente complicada. Você não pode obter uma resposta simples para "como faço para que um argumento de linha de comando aceite um único valor e como faço para acessar esse valor". </gripe>
osman
2
@osman Este tutorial suave sobre argparse pode ajudar ...
lifebalance
2
@ArtOfWarfare "argumentos opcionais" nesse contexto presumivelmente significa argumentos especificados com argumentos do tipo opção, como -for --foo, enquanto "número exato de argumentos deve ser conhecido antecipadamente" presumivelmente significa argumentos posicionais fornecidos sem nenhum sinalizador de opção anterior.
mtraceur
67

Há também argparse módulo stdlib (um "aprimoramento" no optparsemódulo stdlib ). Exemplo da introdução ao argparse :

# script.py
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'integers', metavar='int', type=int, choices=range(10),
         nargs='+', help='an integer in the range 0..9')
    parser.add_argument(
        '--sum', dest='accumulate', action='store_const', const=sum,
        default=max, help='sum the integers (default: find the max)')

    args = parser.parse_args()
    print(args.accumulate(args.integers))

Uso:

$ script.py 1 2 3 4
4

$ script.py --sum 1 2 3 4
10
jfs
fonte
1
é apenas uma cópia e colar
blitu12345
3
@ blitu12345 no momento da publicação da minha resposta, não havia outras respostas que mencionassem argparse de alguma forma. O módulo em si não estava no stdlib¶ O que você tem em relação aos exemplos de código da documentação? Por que você acha necessário criar seus próprios exemplos, em vez dos exemplos fornecidos pelo autor do módulo? E não gosto de respostas apenas para links (não estou sozinha).
JFS
1
As pessoas que vieram para cá já tinham uma idéia do que está na documentação e estarão aqui apenas para obter mais esclarecimentos sobre o tópico. O mesmo foi o meu caso, mas o que realmente encontrei aqui é uma cópia e colagem dos documentos originais.
blitu12345
2
"Os povos que vêm aqui já tinham uma idéia do que está na documentação" - duvido muito dessa suposição. de alguma forma.
S20 /
49

Uma maneira de fazer isso é usando sys.argv. Isso imprimirá o nome do script como o primeiro argumento e todos os outros parâmetros que você passar para ele.

import sys

for arg in sys.argv:
    print arg
JPCosta
fonte
49

A biblioteca docopt é realmente boa. Ele cria um argumento de argumento a partir da cadeia de uso do seu aplicativo.

Por exemplo, do documento readme:

"""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)
ralbatross
fonte
4
Esse se tornou rapidamente o meu caminho favorito. É uma análise de string, por isso é meio frágil, mas é frágil em um só lugar e você pode visualizar sua lógica em try.docopt.org . Argumentos opcionais e mutuamente exclusivos são feitos de uma maneira realmente elegante.
gvoysey
4
Estou desesperada para ver o resto do código para naval_fate.py
John Lawrence Aspden
48

Se você precisar de algo rápido e não muito flexível

main.py:

import sys

first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)

Então corra python main.py James Smith

para produzir a seguinte saída:

Olá James Smith

Kent Munthe Caspersen
fonte
Um uso mais realista seria python main.py "James Smith"o que coloca James Smithem sys.argv[1]e produz uma IndexErrorquando você tentar usar o inexistente sys.argv[2]. O comportamento de cotação dependerá de qual plataforma e shell você executa o Python.
Tripleee
10
Não concordo que meu uso seja menos realista. Finja que seu programa precisa saber o nome e o sobrenome exatos de uma pessoa para executar o script em um negócio em que as pessoas possam ter vários nomes e sobrenomes? Se James Smith tiver Joseph como nome ou sobrenome extra, como distinguir se Joseph é um nome ou sobrenome extra, se você apenas o fizer python main.py "James Joseph Smith"? Se você estiver preocupado com o índice fora dos limites, poderá adicionar uma verificação do número de argumentos fornecidos. Menos realista ou não, meu exemplo mostra como lidar com vários argumentos.
Kent Munthe Caspersen
1
Todas as outras respostas são para planejar uma missão de pouso lunar. Estou apenas usando gmail-trash-msg.py MessageID. Esta resposta é simples: o MessageIDparâmetro de teste foi passado sys.argv[1].
WinEunuuchs2Unix
26
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]
whi
fonte
19

Eu mesmo uso o optparse, mas gosto muito da direção que Simon Willison está tomando com sua recentemente introduzida biblioteca optfunc . Funciona por:

"inspecionando uma definição de função (incluindo seus argumentos e seus valores padrão) e usando isso para construir um analisador de argumentos de linha de comando."

Então, por exemplo, esta definição de função:

def geocode(s, api_key='', geocoder='google', list_geocoders=False):

é transformado neste texto de ajuda optparse:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER
Van Gale
fonte
8

Eu gosto do getopt do stdlib, por exemplo:

try:
    opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err: 
    usage(err)

for opt, arg in opts:
    if opt in ('-h', '--help'): 
        usage()

if len(args) != 1:
    usage("specify thing...")

Ultimamente, tenho envolvido algo semelhante a isso para tornar as coisas menos detalhadas (por exemplo, tornando implícito "-h").

Peter Ericson
fonte
8

De Pocoo clique é mais intuitivo, requer menos clichê e é pelo menos tão poderoso quanto o argparse.

A única fraqueza que encontrei até agora é que você não pode fazer muita personalização para ajudar as páginas, mas isso geralmente não é um requisito e o docopt parece ser a escolha certa .

Ryne Everett
fonte
7

Como você pode ver optparse "O módulo optparse foi descontinuado e não será desenvolvido mais; o desenvolvimento continuará com o módulo argparse ".

tverrbjelke
fonte
5
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                   help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                   const=sum, default=max,
                   help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h

Ref-link: https://docs.python.org/3.3/library/argparse.html
JON
fonte
4

Você pode estar interessado em um pequeno módulo Python que escrevi para facilitar ainda mais o manuseio de argumentos de linha de comando (código aberto e livre para usar) - Comando

Mufasa
fonte
1
Já existe outro módulo de análise de linha de comando chamado Comando: github.com/lakshmivyas/commando . Ele quebra argparse usando decoradores.
Roberto Bonvallet
1
pitão e roda re-invento
Derek
4

Eu recomendo olhar docopt como uma alternativa simples para esses outros.

O docopt é um novo projeto que funciona analisando sua mensagem de uso --help, em vez de exigir que você implemente tudo sozinho. Você apenas precisa colocar sua mensagem de uso no formato POSIX.

David C. Bishop
fonte
4

Ainda outra opção é argh . Ele se baseia no argparse e permite que você escreva coisas como:

import argh

# declaring:

def echo(text):
    "Returns given word as is."
    return text

def greet(name, greeting='Hello'):
    "Greets the user with given name. The greeting is customizable."
    return greeting + ', ' + name

# assembling:

parser = argh.ArghParser()
parser.add_commands([echo, greet])

# dispatching:

if __name__ == '__main__':
    parser.dispatch()

Ele gera automaticamente ajuda e assim por diante, e você pode usar decoradores para fornecer orientações extras sobre como a análise de argumentos deve funcionar.

ruína circular
fonte
essa é a melhor solução. Usar arghé mais fácil que outras bibliotecas ou usar sys.
Juanjo Salvador 24/06
Eu queria gostar, arghmas não é particularmente adequado para cenários em que seu maior desejo é não ter um comando com subcomandos.
Tripleee
1
@tripleee YMMV, mas descobri que isso era mais um defeito na documentação do que na própria biblioteca. Parece perfeitamente viável def frobnicate_spleches(...)definir uma função que faça o que seu script fizer e, em seguida, fazer if __name__ == '__main__': argh.dispatch_command(frobnicate_spleches)no final do arquivo.
circular-ruin
0

Minha solução é entrypoint2 . Exemplo:

from entrypoint2 import entrypoint
@entrypoint
def add(file, quiet=True): 
    ''' This function writes report.

    :param file: write report to FILE
    :param quiet: don't print status messages to stdout
    '''
    print file,quiet

Texto de ajuda:

usage: report.py [-h] [-q] [--debug] file

This function writes report.

positional arguments:
  file         write report to FILE

optional arguments:
  -h, --help   show this help message and exit
  -q, --quiet  don't print status messages to stdout
  --debug      set logging level to DEBUG
pónei
fonte