Recuperando a linha de Comando em Scripts Python

Recuperando a linha de Comando em Scripts Python

Overview

Neste post, vamos mergulhar no mundo da automação e agilidade com Python, explorando como o módulo argparse torna simples o trabalho com linhas de comando. Através de exemplos claros e concisos, vamos descobrir juntos como tornar nossos scripts ainda mais poderosos e flexíveis, abrindo um leque de possibilidades para nossas tarefas diárias de programação. Prepare-se para adicionar uma ferramenta valiosa ao seu conjunto de habilidades de desenvolvimento Python!

Utilizar linhas de comando em scripts Python é razoavelmente simples. O script abaixo funciona no Python 2.8 ou superior. Existem mais opções e alternativas para ler a linha de comando, mas vamos nos ater ao básico…

import argparse as args

parser = args.ArgumentParser(description='Analyzing commandline...')
parser.add_argument("-b", "--blockfile"
                    , default="blocklist.txt"
                    , required=False
                    , help="File containing the sites that should be blocked. One site per line.")

parsed_args = parser.parse_args()

print(parsed_args.blockfile)

Explicando o fonte acima:

  • Primeiro importamos a classe que processa as linhas de comando;
  • Depois criamos um Parser para analisar a linha de comando. O atributo “description” será exibido quando esta linha for processada;
  • O próximo passo é adicionar os argumentos que você deseja receber no seu script;
    • Os dois primeiros atributos são nome e flag do atributo. Qualquer um destes valores poderá ser utilizado na linha de comando;
    • default é o valor padrão para este argumento, caso ele seja omitido;
    • required define se o argumento é obrigatório ou não. Se ele for obrigatório, o atributo default não é utilizado;
    • help é o texto que será exibido quando o script for executado com -h (ou –help);
  • Uma vez que todos os argumentos forem declarados, basta utilizar o a função parse_args() para recuperar os valores informados na linha de comando. Esta função vai retornar uma variável com com todas os argumentos que você definiu.

Este seria o resultado do código acima, caso o script seja executado sem argumentos:

python demo.py

blocklist.txt

Ao mudar o atributo required para true…

python demo.py

usage: PyGuestWatcher.py [-h] -b BLOCKFILE
PyGuestWatcher.py: error: the following arguments are required: -b/--blockfile

Executando o script passando a linha de comando com chamando o argumento pelo nome (-b) ou pela flag (–blockfile)…

python demo.py -b 'foo.bar'

'foo.bar'
python demo.py --blockfile 'foo.bar'

'foo.bar'

*Update: 13/09/2018*

Antes de começar com o update de conteúdo deste post, gostaria de acrescentar um breve contexto: O exemplo acima foi de um script que fiz para bloquear acessos a sites. Ele lê o arquivo blocklist.txt e adiciona os sites no arquivo host, apontando eles para localhost.

Ok. Vamos a atualização de conteúdo…

O exemplo anterior funciona bem, mas o argparse tem outras opções que podem ser uteis.

Vamos imaginar um novo script com uma nova linha de comando. A primeira coisa que você deve fazer é instanciar o parser:

# Instanciating commandline argument parser
parser = args.ArgumentParser("Analyzing commandline...")

Agora vamos adicionar alguns argumentos com configurações diferentes. No final, mostro um resumo destas possibilidades de configuração.

Argumento obrigatório do tipo numérico

    parser.add_argument("-i", "--number",
                        required=True,
                        dest="arg_num",
                        type=int,
                        help="This is a required numeric argument.")

No código acima existem dois parâmetros que não foram utilizados no exemplo anterior:

  1. dest: Indica qual o nome da variável que vai armazenar o valor deste argumento. Se não for informado, o valor é armazenado em uma variável com o nome do argumento. Neste caso, se eu não tivesse informado o parâmetro dest, o valor seria acessado por uma propriedade chamada “number”;
  2. type: Indica o tipo do valor do argumento. Isso quer dizer a variável arg_num vai possuir um valor numérico e não uma string (que é o padrão);

Argumento obrigatório do tipo string

    parser.add_argument("-s", "--string",
                        required=True,
                        dest="arg_str",
                        type=str,
                        help="This is a required string argument.")

Similar ao argumento anterior, mas com o tipo (type) definido para string (str), que é o padrão.

Argumento opcional do tipo numérico

    parser.add_argument("-oi", "--optional-number",
                        required=False,
                        dest="arg_opt_num",
                        type=int,
                        help="This is an optional numeric argument.")

Argumento opcional do tipo string

    parser.add_argument("-oi2", "--optional-number2",
                        required=False,
                        dest="arg_opt_num2",
                        help="This is an optional numeric argument without a type defined.")

Neste argumento eu falo que estou esperando um número, mas como o type não foi definido para int, então o valor dele será sempre uma string.

Outro argumento opcional do tipo string

    parser.add_argument("-os", "--optional-string",
                        required=False,
                        dest="arg_opt_str",
                        type=str,
                        help="This is an optional string argument.")

Este argumento é opcional (required=False) e foi definido explicitamente como string (type=str)

Mais um argumento opcional do tipo string

    parser.add_argument("-os2", "--optional-string2",
                        required=False,
                        dest="arg_opt_str2",
                        action="store",
                        type=str,
                        help="This is an optional string argument with explicit store action defined.")

Este argumento é similar ao anterior, mas possui um parâmetro a mais, o action. Ele define o que será feito com o argumento informado. O valor padrão é “store”, que simplesmente armazena o valor do argumento em uma variável.

Argumento do tipo flag (define valor para True)

    parser.add_argument("-t", "--true",
                        required=False,
                        dest="arg_true",
                        action="store_true",
                        default=False,
                        help="This is a flag argument. If informed, will be set to true, otherwise, will be false.")

O argumento acima possui uma action diferente, a store_true. Isso indica que se você utilizar esta flag, o valor armazenado na variável dest será True. O valor padrão para este argumento é (por razões obvias) False.

Por flag eu quero dizer que é um argumento que você utiliza apenas ele. Ao contrário de um argumento do tipo numérico (-i 42), você vai utilizar apenas o argumento em si (-t ou –true)

Argumento do tipo flag (define valor para False)

    parser.add_argument("-f", "--false",
                        required=False,
                        dest="arg_false",
                        action="store_false",
                        default=True,
                        help="This is a flag argument. If informed, will be set to false, otherwise, will be true.")

Este argumento é similar ao anterior, mas seus valores são invertidos: Por padrão, este argumento é considerado como True e se for informado, muda para False. Isso foi definido na action com valor store_false.

Argumento do tipo flag sem valor default definido

    parser.add_argument("-n", "--no-default",
                        required=False,
                        dest="arg_flag",
                        action="store_false",
                        help="This is a flag argument. If informed, will be set to false. No default configured for this.")

O argumento acima não possui um valor padrão, mas seu action está definido como store_false, ou seja, se for omitido, a variável arg_flag vai ficara com valor True.

Isso demonstra que o argparse é esperto o suficiente para entender que, se você quer armazenar False na variável, caso o argumento seja utilizado, então esta mesma variável deve possuir o valor oposto (True) como padrão.

Argumento do tipo lista

    parser.add_argument("-l", "--list",
                        required=False,
                        dest="arg_list",
                        default=[],
                        action="append",
                        help="This is an optional list argument with default value defined.")

O argumento acima é do tipo lista, ou seja, você pode utiliza-lo varias vezes na linha de comando, pois todos os valores informados serão adicionados a variável. Este comportamento é definido pelo valor append, informado no parametro action.

Argumento do tipo lista (numérico)

    parser.add_argument("-li", "--list-int",
                        required=False,
                        dest="arg_list_int",
                        type=int,
                        action="append",
                        help="This is an optional list of numbers argument with no default value defined.")

Este argumento é similar ao anterior. A primeira diferença é que definido um tipo para os valores passados (type=int) e isso implica em dizer que a variável vai possuir uma lista de integers.

A outra diferença é que não definimos um valor padrão (default=[]) para este argumento, ou seja, se este argumento não for utilizado, a variável arg_list_int terá o valor None ao invés de uma lista vazia ([]).

Argumento do tipo lista de constantes

    parser.add_argument("-lc1", "--list-const1",
                        required=False,
                        dest="arg_list_const",
                        action="append_const",
                        const="Flag 1",
                        help="This is a flag that adds a constant value to a list.")

    parser.add_argument("-lc2", "--list-const2",
                        required=False,
                        dest="arg_list_const",
                        action="append_const",
                        const="Flag 2",
                        help="This is a flag that adds another constant value to a list.")

Acima estão dois argumentos, mas eles fazem parte do mesmo processo. Ambos estão com a action definida para append_const e isso quer dizer que eles vão adicionar um valor pre-definido a variável, resultando em uma lista de valores padrões.

A diferença entre append e append_const é que o append vai adicionar o valor que o usuário informar para a lista, enquanto o append_const vai adicionar o valor que você definiu.

Para que esta action funcione corretamente, o parametro dest deve ser igual para todos os argumentos relevantes.

Argumento do tipo versão

    parser.add_argument("-v", "--version",
                        action="version",
                        version="{} | ver {}".format(__file__, __version__),
                        help="Argument that prints the current version of your application.")

Assim como o argumento que mostra o texto de ajuda (-h ou –help), um argumento com action definido para version vai sobrescrever os outros argumentos e exibir apenas a sua informação, ou seja, você não pode misturar o argumento do tipo versão com os outros na mesma linha de comando.

O que este tipo de argumento faz é padronizar uma forma de exibir a versão da sua aplicação. Quando utilizado, ele vai mostrar o texto definido no parametro version.

Resumindo os parametros da função add_argument

  • Primeiro parametro (posicional) recebe o “apelido” do argumento. (Exemplo: -v);
  • Segundo parametro (posicional) recebe o nome completo do argumento. Se o parâmetro dest não for informado, este será o nome da variável que armazena o valor do argumento;
  • required: Indica se o argumento é ou não obrigatório (True/False);
  • dest: Indica o nome da variável que vai receber o valor do argumento;
  • action: Indica qual ação será tomada no argumento. Os valores possíveis são:
    1. store: (padrão) armazena o valor do argumento na variável;
    2. store_true: Armazena True se o argumento for utilizado. Caso contrário, o valor será False;
    3. store_false: Armazena False se o argumento for utilizado. Caso contrário, o valor será True;
    4. append: Adiciona o valor do argumento a uma lista;
    5. append_const: Adiciona um valor pre-definido a um lista. Devem ser adicionados varios argumentos, um para cada valor que poderá ser adicionado a lista;
    6. version: Exibe a verão do aplicativo (definido no parametro chamado version);
  • const: Utilizado apenas quando o action está definido como append_const. Neste caso, o valor definido neste parametro será adicionado a lista;
  • default: Valor padrão do argumento, caso ele não seja informado pelo usuário;
  • version: Utilizado apenas quando o action está definido para “version”. Neste caso, o valor informado neste parametro será exibido para o usuário;

Para facilitar, fiz um exemplo no Github onde você pode passar estes valores e ver o resultado no console. Ele vai apenas exibir o nome da variável passada via linha de comando, seu valor e o tipo do valor.

Espero ter ajudado!

Referência:

Traduções: