Argumentos de la línea de comandos en Python

En este tutorial, nos sumergimos en los argumentos de la línea de comandos en Python. Usando el módulo sys, el módulo getopt y el módulo argparse, analizaremos y leeremos los argumentos.

Visión general

Dado que Python es un lenguaje de programación tan popular, además de tener soporte para la mayoría de los sistemas operativos, se usa ampliamente para crear herramientas de línea de comandos para muchos propósitos. Estas herramientas pueden variar desde aplicaciones CLI simples hasta aplicaciones más complejas, como la herramienta awscli de AWS.

Las herramientas complejas como esta generalmente son controladas por el usuario a través de [argumentos de línea de comando] (https://en.wikipedia.org/wiki/Command-line_interface#Arguments), que le permite al usuario usar comandos específicos, establecer opciones, y más. Por ejemplo, estas opciones podrían indicarle a la herramienta que genere información adicional, lea datos de una fuente específica o envíe resultados a una ubicación determinada.

En general, los argumentos se pasan a las herramientas CLI de manera diferente, según su sistema operativo:

  • Similar a Unix: - seguido de una letra, como -h, o -- seguido de una palabra, como --help
  • Windows: / seguido de una letra o palabra, como /help

Estos diferentes enfoques existen debido a razones históricas. Muchos programas en sistemas similares a Unix admiten la notación de guión simple y doble. La notación de un solo guión se usa principalmente con opciones de una sola letra, mientras que los guiones dobles presentan una lista de opciones más legible, que es particularmente útil para opciones complejas que necesitan ser más explícitas.

Nota: En este artículo nos centraremos únicamente en el formato similar a Unix de - y --.

Tenga en cuenta que tanto el nombre como el significado de un argumento son específicos de un programa: no existe una definición general, aparte de algunas convenciones comunes como --help para obtener más información sobre el uso de la herramienta. Como desarrollador de una secuencia de comandos de Python, decidirá qué argumentos proporcionar a la persona que llama y qué hacen. Esto requiere una evaluación adecuada.

A medida que crezca su lista de argumentos disponibles, su código se volverá más complejo al tratar de analizarlos con precisión. Afortunadamente, en Python hay varias bibliotecas disponibles para ayudarte con esto. Cubriremos algunas de las soluciones más comunes, que van desde "hágalo usted mismo" con sys.argv, hasta el enfoque "hecho para usted" con argparse.

Manejo de argumentos de línea de comandos con Python

Python 3 admite varias formas diferentes de manejar los argumentos de la línea de comandos. La forma integrada es usar el módulo sys. En términos de nombres y su uso, se relaciona directamente con la biblioteca C (libc). La segunda forma es el módulo getopt, que maneja opciones cortas y largas, incluida la evaluación de los valores de los parámetros.

Además, existen otros dos métodos comunes. Este es el módulo argparse, que se deriva del módulo optparse disponible hasta Python 2.7. El otro método es usando el módulo docopt, que está disponible en GitHub.

Cada una de estas formas tiene sus pros y sus contras, por lo que vale la pena evaluar cada una para ver cuál se adapta mejor a tus necesidades.

El módulo sys

Este es un módulo básico que se envió con Python desde los primeros días. Se necesita un enfoque muy similar a la biblioteca C usando argc/argv para acceder a los argumentos. El módulo de sistema implementa los argumentos de la línea de comandos en una estructura de lista simple llamada sys.argv.

Cada elemento de la lista representa un único argumento. El primer elemento de la lista, sys.argv[0], es el nombre del script de Python. El resto de los elementos de la lista, sys.argv[1] a sys.argv[n], son los argumentos de la línea de comandos del 2 al n. Como delimitador entre los argumentos, se utiliza un espacio. Los valores de los argumentos que contienen un espacio deben estar entre comillas para que sys los analice correctamente.

El equivalente de argc es solo el número de elementos en la lista. Para obtener este valor, utilice el operador len() de Python. Más adelante mostraremos esto en un ejemplo de código.

Imprimiendo el primer argumento CLI

En este primer ejemplo, nuestro script determinará la forma en que fue llamado. Esta información se mantiene en el primer argumento de la línea de comando, indexado con 0. El siguiente código muestra cómo obtiene el nombre de su secuencia de comandos de Python.

1
2
3
import sys

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

Guarde este código en un archivo llamado arguments-program-name.py y luego llámelo como se muestra a continuación. El resultado es el siguiente y contiene el nombre del archivo, incluida su ruta completa:

1
2
3
4
$ 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 puede ver en la segunda llamada anterior, no solo obtenemos el nombre del archivo de Python, sino también la ruta completa utilizada para llamarlo.

Contando el número de argumentos

En este segundo ejemplo, simplemente contamos el número de argumentos de la línea de comando usando el método integrado len(). sys.argv es la lista que tenemos que examinar. En el siguiente código, obtenemos el número de argumentos y luego restamos 1 porque uno de esos argumentos (es decir, el primero) siempre se establece como el nombre del archivo, lo que no siempre nos resulta útil. Por lo tanto, el número real de argumentos pasados ​​por el usuario es len(sys.argv) - 1.

1
2
3
4
5
import sys

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

Guarde y nombre este archivo arguments-count.py. A continuación se muestran algunos ejemplos de cómo llamar a este script. Esto incluye tres escenarios diferentes:

  • Una llamada sin más argumentos de línea de comando
  • Una llamada con dos argumentos
  • Una llamada con dos argumentos, donde el segundo es una cadena entrecomillada que contiene un espacio
1
2
3
4
5
6
$ 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 a través de argumentos {#iterando a través de argumentos}

Nuestro tercer ejemplo genera todos los argumentos enviados a la secuencia de comandos de Python, excepto el nombre del programa en sí. Por lo tanto, recorremos los argumentos de la línea de comando comenzando con el segundo elemento de la lista. Recuerde que este es el índice 1 ya que las listas están basadas en 0 en Python.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import sys

# Count the arguments
arguments = len(sys.argv) - 1

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

A continuación, llamamos a nuestro código, que se guardó en el archivo arguments-output.py. Como hicimos con nuestro ejemplo anterior, la salida ilustra tres llamadas diferentes:

  • Una llamada sin argumentos
  • Una llamada con dos argumentos
  • Una llamada con dos argumentos, donde el segundo argumento es una cadena entre comillas que contiene un espacio
1
2
3
4
5
6
7
$ 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

Recuerde, el objetivo de mostrar el ejemplo de la cadena entre comillas es que los parámetros generalmente están delimitados por un espacio, a menos que estén entre comillas.

El módulo getopt

Como habrás notado antes, el módulo sys divide la cadena de la línea de comando en facetas individuales solamente. El módulo getopt de Python va un poco más allá y amplía la separación de la cadena de entrada por validación de parámetros. Basado en la función C getopt, permite opciones cortas y largas, incluida una asignación de valor.

En la práctica, requiere el módulo sys para procesar correctamente los datos de entrada. Para hacerlo, tanto el módulo sys como el módulo getopt deben cargarse previamente. A continuación, de la lista de parámetros de entrada, eliminamos el primer elemento de la lista (consulte el código a continuación) y almacenamos la lista restante de argumentos de la línea de comando en la variable llamada argument_list.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Include standard modules
import getopt, sys

# Get full command-line arguments
full_cmd_arguments = sys.argv

# Keep all but the first
argument_list = full_cmd_arguments[1:]

print argument_list

Los argumentos en argument_list ahora se pueden analizar usando el método getopts(). Pero antes de hacer eso, necesitamos decirle a getopts() qué parámetros son válidos. Se definen así:

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

Esto significa que estos argumentos son los que consideramos válidos, junto con información adicional:

1
2
3
4
5
6
7
------------------------------------------
long argument   short argument  with value
------------------------------------------
--help           -h              no
--output         -o              yes
--verbose        -v              no
------------------------------------------

Es posible que haya notado que la opción abreviada o fue precedida por dos puntos, :. Esto le dice a getopt que a esta opción se le debe asignar un valor.

Esto ahora nos permite procesar una lista de argumentos. El método getopt() requiere que se configuren tres parámetros: la lista de argumentos reales de argv, así como las opciones cortas y largas válidas (que se muestran en el fragmento de código anterior).

La llamada al método en sí se mantiene en una declaración de captura para cubrir errores durante la evaluación. Se genera una excepción si se descubre un argumento que no forma parte de la lista definida anteriormente. El script de Python imprimirá el mensaje de error en la pantalla y saldrá con el código de error 2.

1
2
3
4
5
6
try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    # Output error, and return with an error code
    print (str(err))
    sys.exit(2)

Finalmente, los argumentos con los valores correspondientes se almacenan en las dos variables denominadas argumentos y valores. Ahora, puede evaluar fácilmente estas variables en su código. Podemos usar un bucle for para iterar a través de la lista de argumentos reconocidos, una entrada tras otra.

1
2
3
4
5
6
7
8
# Evaluate given options
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))

A continuación puede ver el resultado de ejecutar este código. Mostraremos cómo reacciona el programa con argumentos de programa válidos e inválidos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ 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

La última llamada a nuestro programa puede parecer un poco confusa al principio. Para entenderlo, debe saber que las opciones abreviadas (a veces también llamadas banderas) se pueden usar junto con un solo guión. Esto permite que su herramienta acepte más fácilmente muchas opciones. Por ejemplo, llamar a python arguments-getopt.py -vh es lo mismo que llamar a python arguments-getopt.py -v -h. Entonces, en la última llamada anterior, el módulo getopt pensó que el usuario estaba tratando de pasar -e como una opción, lo cual no es válido.

El módulo argparse

El módulo argparse ha estado disponible desde Python 3.2, y una mejora del módulo optparse que existe hasta Python 2.7. La documentación de Python contiene una descripción de la API y un tutorial que cubre todos los métodos en detalle.

El módulo ofrece una interfaz de línea de comandos con una salida estandarizada, mientras que las dos soluciones anteriores dejan gran parte del trabajo en sus manos. argparse permite la verificación de argumentos fijos y opcionales, con verificación de nombres en estilo corto o largo. Como argumento opcional predeterminado, incluye -h, junto con su versión larga --help. Este argumento va acompañado de un mensaje de ayuda predeterminado que describe los argumentos aceptados.

El siguiente código muestra la inicialización del analizador y el resultado a continuación que muestra la llamada básica, seguida del mensaje de ayuda. A diferencia de las llamadas de Python que usamos en los ejemplos anteriores, recuerde usar Python 3 con estos ejemplos.

1
2
3
4
5
6
# Include standard modules
import argparse

# Initiate the parser
parser = argparse.ArgumentParser()
parser.parse_args()
1
2
3
4
5
6
7
8
9
$ 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

En el siguiente paso, agregaremos una descripción personalizada al mensaje de ayuda para nuestros usuarios. Inicializar el analizador de esta manera permite un texto adicional. El siguiente código almacena la descripción en la variable texto, que se proporciona explícitamente a la clase argparse como el parámetro descripción. Al llamar a este código a continuación, puede ver cómo se ve la salida.

1
2
3
4
5
6
7
8
9
# Include standard modules
import argparse

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

# Initiate the parser with a description
parser = argparse.ArgumentParser(description=text)
parser.parse_args()
1
2
3
4
5
6
7
8
$ 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 paso final, agregaremos un argumento opcional llamado -V, que tiene un argumento de estilo largo correspondiente llamado --version. Para hacerlo, usamos el método add_argument() que llamamos con tres parámetros (que se muestran solo para --version):

  • El nombre del parámetro: --version
  • El texto de ayuda para el parámetro: ayuda="mostrar versión del programa"
  • Acción (sin valor adicional): action="store_true"

El código fuente para eso se muestra a continuación. La lectura de los argumentos en la variable llamada args se realiza a través del método parse_args() del objeto parser. Tenga en cuenta que envía tanto la versión corta como la larga en una sola llamada. Finalmente, verifica si los atributos args.V o args.version están establecidos y emite el mensaje de versión.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Include standard modules
import argparse

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

# Read arguments from the command line
args = parser.parse_args()

# Check for --version or -V
if args.version:
    print("This is myprogram version 0.1")
1
2
3
4
$ python3 arguments-argparse-optional.py -V
This is myprogram version 0.1
$ python3 arguments-argparse-optional.py --version
This is myprogram version 0.1

El argumento --version no requiere que se proporcione un valor en la línea de comando. Es por eso que establecemos el argumento de acción en "store_true". En otros casos, es posible que necesite un valor asignado adicional, por ejemplo, si especifica un determinado volumen, altura o ancho. Esto se muestra en el siguiente ejemplo. Como caso predeterminado, tenga en cuenta que todos los argumentos se interpretan como cadenas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Include standard modules
import argparse

# Initiate the parser
parser = argparse.ArgumentParser()

# Add long and short argument
parser.add_argument("--width", "-w", help="set output width")

# Read arguments from the command line
args = parser.parse_args()

# Check for --width
if args.width:
    print("Set output width to %s" % args.width)

Aquí mostramos lo que sucede cuando se envían diferentes valores de argumento. Esto incluye tanto la versión corta como la larga, así como el mensaje de ayuda.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ 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

Conclusión

En este artículo, mostramos muchos métodos diferentes para recuperar argumentos de la línea de comandos en Python, incluido el uso de sys, getopt y argparse. Estos módulos varían en funcionalidad, algunos brindan mucho más que otros. sys es totalmente flexible, mientras que tanto getopt como argparse requieren cierta estructura. Por el contrario, cubren la mayor parte del trabajo complejo que sys te deja. Después de trabajar con los ejemplos proporcionados, debería poder determinar qué módulo se adapta mejor a su proyecto.

En este artículo no hablamos de otras soluciones como el módulo docopts, solo lo mencionamos. Este módulo sigue un enfoque totalmente diferente y se explicará en detalle en uno de los próximos artículos.

Referencias