Argumentos de linha de comando em Python

Visão geral

Sendo o Python uma linguagem de programação muito popular, além de ter suporte para a maioria dos sistemas operacionais e muitas bibliotecas que facilitam o processamento de argumentos de linha de comando, ele se tornou amplamente utilizado para criar ferramentas de linha de comando para diversas finalidades. Essas ferramentas podem variar de aplicativos CLI simples até aqueles mais complexos, como o AWS awscli ferramenta.

Ferramentas complexas como essa são normalmente controladas pelo usuário por meio de argumentos de linha de comando, que permite ao usuário usar comandos específicos, definir opções e muito mais. Por exemplo, essas opções podem instruir a ferramenta a gerar informações adicionais, ler dados de uma fonte especificada ou enviar a saída para um determinado local.

Em geral, os argumentos são passados ​​para ferramentas CLI de forma diferente, dependendo do seu sistema operacional:

  • Semelhante ao Unix: - seguido por uma letra, como -hou -- seguido por uma palavra, como --help
  • Windows: / seguido por uma letra ou palavra, como /help

Essas diferentes abordagens existem por razões históricas. Muitos programas em sistemas do tipo Unix suportam a notação de traço simples e duplo. A notação de traço único é usada principalmente com opções de letra única, enquanto os traços duplos apresentam uma lista de opções mais legível, o que é particularmente útil para opções complexas que precisam ser mais explícitas.

Note: Neste artigo focaremos apenas no formato semelhante ao Unix de - e --.

Tenha em mente que tanto o nome quanto o significado de um argumento são específicos de um programa – não existe uma definição geral, exceto algumas convenções comuns como --help para obter mais informações sobre o uso da ferramenta. Como desenvolvedor de um script Python, você decidirá quais argumentos fornecer ao chamador e o que eles farão. Isto requer uma avaliação adequada.

À medida que sua lista de argumentos disponíveis aumenta, seu código se torna mais complexo na tentativa de analisá-los com precisão. Felizmente, em Python existem várias bibliotecas disponíveis para ajudá-lo com isso. Abordaremos algumas das soluções mais comuns, que vão desde o “faça você mesmo” com sys.argv, para a abordagem “feito para você” com argparse.

Lidando com argumentos de linha de comando com Python

Python 3+ e o ecossistema ao redor suportam diversas maneiras diferentes de lidar com argumentos de linha de comando. Há muitos bibliotecas que facilitam a análise de argumentos de linha de comando.

A maneira integrada é usar o sys módulo. Em termos de nomes e seu uso, está diretamente relacionado à biblioteca C (libc).

A segunda maneira é a getopt módulo, que lida com opções curtas e longas, incluindo a avaliação dos valores dos parâmetros.

A módulo argparse, que é derivado do optparse módulo (disponível até Python 2.7).

A docopt módulo, que é disponível no GitHub, também permite a mesma funcionalidade.

Recentemente, o absl biblioteca também vem ganhando força, como forma de substituir optparse e getopt().

Cada uma dessas formas tem seus prós e contras, por isso vale a pena avaliar cada uma para ver qual atende melhor às suas necessidades.

O módulo sys

Este é um módulo básico que acompanha o Python desde os primeiros dias. É necessária uma abordagem muito semelhante à biblioteca C usando argc/argv para acessar os argumentos. O módulo sys implementa os argumentos da linha de comando em uma estrutura de lista simples chamada sys.argv.

Cada elemento da lista representa um único argumento. O primeiro item da lista, sys.argv[0], é o nome do script Python. O resto dos elementos da lista, sys.argv[1] para sys.argv[n], são os argumentos da linha de comando de 2 a n.

Como delimitador entre os argumentos, é utilizado um espaço. Os valores de argumentos que contêm um espaço devem ser colocados entre aspas para serem analisados ​​adequadamente por sys.

O equivalente a argc é apenas o número de elementos na lista. Para obter este valor, use o Python len() operador. Mostraremos isso em um exemplo de código mais tarde.

Imprimindo o primeiro argumento CLI

Neste primeiro exemplo, nosso script determinará a forma como foi chamado. Esta informação é mantida no primeiro argumento da linha de comando, indexado com 0. O código abaixo mostra como você obtém o nome do seu script Python:

import sys

print("The script has the name %s" % (sys.argv[0])

Salve este código em um arquivo chamado arguments-program-name.pye chame-o conforme mostrado abaixo. A saída é a seguinte e contém o nome do arquivo, incluindo seu caminho completo:

$ python arguments-program-name.py
The script has the name arguments-program-name.py
$ python /home/user/arguments-program-name.py
The script has the name /home/user/arguments-program-name.py

Como você pode ver na segunda chamada acima, não obtemos apenas o nome do arquivo Python, mas também o caminho completo usado para chamá-lo.

Contando o número de argumentos

Neste segundo exemplo, simplesmente contamos o número de argumentos de linha de comando usando o recurso integrado len() método. sys.argv é a lista que devemos examinar. No código abaixo, obtemos o número de argumentos e subtraímos 1 porque um desses argumentos (ou seja, o primeiro) é sempre definido como o nome do arquivo, o que nem sempre é útil para nós. Assim, o número real de argumentos passados ​​pelo usuário é len(sys.argv) - 1:

import sys


arguments = len(sys.argv) - 1
print ("The script is called with %i arguments" % (arguments))

Salve e nomeie este arquivo argumentos-count.py. Alguns exemplos de chamada deste script são mostrados abaixo. Isso inclui três cenários diferentes:

  • Uma chamada sem mais argumentos de linha de comando
  • Uma chamada com dois argumentos
  • Uma chamada com dois argumentos, onde o segundo é uma string entre aspas contendo um espaço
$ python arguments-count.py
The script is called with 0 arguments
$ python arguments-count.py --help me
The script is called with 2 arguments
$ python arguments-count.py --option "long string"
The script is called with 2 arguments
Iterando por meio de argumentos

Nosso terceiro exemplo gera todos os argumentos enviados ao script Python, exceto o próprio nome do programa. Portanto, percorremos os argumentos da linha de comando começando com o segundo elemento de lista. Lembre-se de que este é o índice 1, pois as listas são baseadas em 0 em Python:

import sys


arguments = len(sys.argv) - 1


position = 1
while (arguments >= position):
    print ("Parameter %i: %s" % (position, sys.argv[position]))
    position = position + 1

Abaixo chamamos nosso código, que foi salvo no arquivo argumentos-output.py. Tal como feito no nosso exemplo anterior, a saída ilustra três chamadas diferentes:

  • Uma chamada sem argumentos
  • Uma chamada com dois argumentos
  • Uma chamada com dois argumentos, onde o segundo argumento é uma string entre aspas contendo um espaço
$ python arguments-output.py
$ python arguments-output.py --help me
Parameter 1: --help
Parameter 2: me
$ python arguments-output.py --option "long string"
Parameter 1: --option
Parameter 2: long string

Lembre-se, o objetivo de mostrar o exemplo de string entre aspas é que os parâmetros geralmente são delimitados por um espaço, a menos que eles estão cercados por aspas.

Bandeiras de rapel (absl)

A biblioteca Flags do Abseil tem como objetivo trazer argumentos de linha de comando para produção, com argumentos de linha de comando distribuídos. Quando um módulo usa sinalizadores de linha de comando e é importado para outro módulo – o outro módulo importa as bandeiras também, e pode processá-los encaminhando-os para o módulo importado.

Isso torna os argumentos complexos de linha de comando compartilhados entre módulos mais fáceis e menos detalhados.

Além disso, a biblioteca permite definir os valores padrão, as descrições e o tipo de dados dos argumentos, portanto, verificações e conversões adicionais não são necessárias.

from absl import flags
import sys


flags.DEFINE_string('name', 'User', 'The name of the user.')


FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"Hello {FLAGS.name}!")

Os tipos de dados suportados são:

  • DEFINE_integer()
  • DEFINE_string()
  • DEFINE_bool()
  • DEFINE_enum()
  • DEFINE_list()
  • DEFINE_float()

Bem como DEFINE_multi_integer(), DEFINE_multi_string() e DEFINE_multi_enum() para entrada com vários argumentos. Além disso, executando --help, --helpfull, etc. imprima os sinalizadores existentes e suas descrições, em diferentes formatos.

Confira nosso guia prático e prático para aprender Git, com práticas recomendadas, padrões aceitos pelo setor e folha de dicas incluída. Pare de pesquisar comandos Git no Google e realmente aprender -lo!

A biblioteca também permite definir validações – tanto em termos de intervalo, como valores baseados em números inteiros com um upper_bound or lower_bound isso é aceitável e executar métodos arbitrários para verificar valores:

def validate_name(value):
    return len(value) > 15

flags.register_validator('name',
                         validate_name,
                         message='Name is over 15 characters long.',
                         flag_values=FLAGS)

Reunindo isso em um exemplo concreto:

from absl import flags
import sys

flags.DEFINE_string('name', 'User', 'The name of the user.')
flags.DEFINE_integer('tasks', 0, 'The number of tasks a user has.', lower_bound=0)

FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"{FLAGS.name} has {FLAGS.tasks} tasks to work on.")
$ python flags.py --name=John --tasks=5
John has 5 tasks to work on.
$ python flags.py --name=John --tasks=-1

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_flag.py", line 180, in _parse
    return self.parser.parse(argument)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_argument_parser.py", line 168, in parse
    raise ValueError('%s is not %s' % (val, self.syntactic_help))
ValueError: -1 is not a non-negative integer
...

O módulo argparse

A módulo argparse está disponível desde Python 3.2, e um aprimoramento do optparse módulo que existe até Python 2.7. A documentação do Python contém uma descrição da API e um tutorial que cobre todos os métodos detalhadamente.

O módulo oferece uma interface de linha de comando com saída padronizada, enquanto as duas soluções anteriores deixam grande parte do trabalho em suas mãos. argparse permite a verificação de argumentos fixos e opcionais, com verificação de nomes no estilo curto ou longo. Como argumento opcional padrão, inclui -h, junto com sua versão longa --help. Este argumento é acompanhado por uma mensagem de ajuda padrão descrevendo os argumentos aceitos.

O código abaixo mostra a inicialização do analisador e a saída abaixo que mostra a chamada básica, seguida pela mensagem de ajuda. Em contraste com as chamadas Python que usamos nos exemplos anteriores, lembre-se de usar Python 3 com estes exemplos:


import argparse


parser = argparse.ArgumentParser()
parser.parse_args()
$ python3 arguments-argparse-basic.py 
$ python3 arguments-argparse-basic.py -h
usage: arguments-argparse-basic.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$ python3 arguments-argparse-basic.py --verbose
usage: arguments-argparse-basic.py [-h]
arguments-argparse-basic.py: error: unrecognized arguments: --verbose

Na próxima etapa, adicionaremos uma descrição personalizada à mensagem de ajuda para nossos usuários. Inicializar o analisador desta forma permite um texto adicional. O código abaixo armazena a descrição no arquivo text variável, que é dada explicitamente ao argparse classe como o description parâmetro. Chamando o código abaixo, você pode ver a aparência da saída:


import argparse


text = 'This is a test program. It demonstrates how to use the argparse module with a program description.'


parser = argparse.ArgumentParser(description=text)
parser.parse_args()
$ python3 arguments-argparse-description.py --help
usage: arguments-argparse-description.py [-h]

This is a test program. It demonstrates how to use the argparse module with a
program description.

optional arguments:
  -h, --help  show this help message and exit

Como etapa final, adicionaremos um argumento opcional chamado -V, que tem um argumento de estilo longo correspondente chamado --version. Para fazer isso usamos o método add_argument() que chamamos com três parâmetros (exibidos para --version, apenas):

  • O nome do parâmetro: --version
  • O texto de ajuda do parâmetro: help="show program version"
  • Ação (sem valor adicional): action="store_true"

O código-fonte para isso é exibido abaixo. Lendo os argumentos na variável chamada args é feito através do parse_args() método do parser objeto. Observe que você envia a versão curta e a versão longa em uma chamada. Finalmente, você verifica se os atributos args.V or args.version são definidos e geram a mensagem de versão:


import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-V", "--version", help="show program version", action="store_true")


args = parser.parse_args()


if args.version:
    print("This is myprogram version 0.1")
$ python3 arguments-argparse-optional.py -V
This is myprogram version 0.1
$ python3 arguments-argparse-optional.py --version
This is myprogram version 0.1

A --version argumento não requer que um valor seja fornecido na linha de comando. É por isso que definimos o argumento da ação como "store_true". Em outros casos, você pode precisar de um valor atribuído adicional, por exemplo, se especificar um determinado volume, altura ou largura. Isso é mostrado no próximo exemplo. Como caso padrão, observe que todos os argumentos são interpretados como strings:


import argparse


parser = argparse.ArgumentParser()


parser.add_argument("--width", "-w", help="set output width")


args = parser.parse_args()


if args.width:
    print("Set output width to %s" % args.width)

Aqui mostramos o que acontece ao enviar diferentes valores de argumentos. Isso inclui a versão curta e longa, bem como a mensagem de ajuda:

$ python3 arguments-argparse-optional2.py -w 10
Set output width to 10
$ python3 arguments-argparse-optional2.py --width 10
Set output width to 10
$ python3 arguments-argparse-optional2.py -h
usage: arguments-argparse-optional2.py [-h] [--width WIDTH]

optional arguments:
  -h, --help            show this help message and exit
  --width WIDTH, -w WIDTH
                        set output width

O módulo getopt

Como você já deve ter notado antes, o sys O módulo divide a string da linha de comando apenas em facetas únicas. O Píton módulo getopt vai um pouco além e estende a separação da string de entrada pela validação de parâmetros. Com base no getopt Função C, permite opções curtas e longas, incluindo uma atribuição de valor.

Na prática, exige a sys módulo para processar os dados de entrada corretamente. Para tanto, tanto o sys módulo e o getopt módulo deve ser carregado previamente. A seguir, da lista de parâmetros de entrada, removemos o primeiro elemento da lista (veja o código abaixo) e armazenamos a lista restante de argumentos de linha de comando na variável chamada argument_list:


import getopt, sys


full_cmd_arguments = sys.argv


argument_list = full_cmd_arguments[1:]

print argument_list

Os argumentos em argument_list agora pode ser analisado usando o getopts() método. Mas antes de fazer isso, precisamos dizer getopts() sobre quais parâmetros são válidos. Eles são definidos assim:

short_options = "ho:v"
long_options = ["help", "output=", "verbose"]

Isso significa que esses argumentos são aqueles que consideramos válidos, juntamente com algumas informações extras:

------------------------------------------
long argument   short argument  with value
------------------------------------------
--help           -h              no
--output         -o              yes
--verbose        -v              no
------------------------------------------

Você deve ter notado que o o a opção curta foi precedida por dois pontos, :. Isso informa getopt que esta opção deve receber um valor.

Isso agora nos permite processar uma lista de argumentos. O getopt() método requer que três parâmetros sejam configurados – a lista de argumentos reais de argv, bem como as opções curtas e longas válidas (mostradas no trecho de código anterior).

A própria chamada do método é mantida em uma instrução try-catch para cobrir erros durante a avaliação. Uma exceção é levantada se for descoberto um argumento que não faz parte da lista definida anteriormente. O script Python imprimirá a mensagem de erro na tela e sairá com o código de erro 2:

try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    
    print (str(err))
    sys.exit(2)

Finalmente, os argumentos com os valores correspondentes são armazenados nas duas variáveis ​​denominadas arguments e values. Agora você pode avaliar facilmente essas variáveis ​​em seu código. Podemos usar um for-loop para percorrer a lista de argumentos reconhecidos, uma entrada após a outra.


for current_argument, current_value in arguments:
    if current_argument in ("-v", "--verbose"):
        print ("Enabling verbose mode")
    elif current_argument in ("-h", "--help"):
        print ("Displaying help")
    elif current_argument in ("-o", "--output"):
        print (("Enabling special output mode (%s)") % (current_value))

Abaixo você pode ver a saída da execução deste código. Mostraremos como o programa reage com argumentos de programa válidos e inválidos:

$ python arguments-getopt.py -h
Displaying help
$ python arguments-getopt.py --help
Displaying help
$ python arguments-getopt.py --output=green --help -v
Enabling special output mode (green)
Displaying help
Enabling verbose mode
$ python arguments-getopt.py -verbose
option -e not recognized

A última chamada do nosso programa pode parecer um pouco confusa no início. Para entendê-lo, você precisa saber que as opções abreviadas (às vezes também chamadas de sinalizadores) podem ser usadas em conjunto com um único travessão. Isso permite que sua ferramenta aceite muitas opções com mais facilidade. Por exemplo, ligar python arguments-getopt.py -vh é o mesmo que ligar python arguments-getopt.py -v -h. Então, na última chamada acima, o getopt módulo pensou que o usuário estava tentando passar -e como uma opção, o que é inválido.

Conclusão

Neste artigo, mostramos muitos métodos diferentes para recuperar argumentos de linha de comando em Python, incluindo o uso sys, getopt e argparse. Esses módulos variam em funcionalidade, alguns oferecendo muito mais que outros. sys é totalmente flexível, enquanto ambos getopte argparse requerem alguma estrutura. Em contraste, eles cobrem a maior parte do trabalho complexo que sys deixa para você. Depois de trabalhar com os exemplos fornecidos, você será capaz de determinar qual módulo se adapta melhor ao seu projeto.

Neste artigo não falamos sobre outras soluções como o docopts módulo, acabamos de mencioná-lo. Este módulo segue uma abordagem totalmente diferente e será explicado detalhadamente em um dos próximos artigos.

Referências

Carimbo de hora:

Mais de Abuso de pilha