Tú has estado ahí, yo también, y volveremos a pasar: se te presenta una tarea cualquiera y acabas viendo que la manera más sencilla de automatizarla es mediante un script de terminal.

Con suerte, podrás usar Fish, pero todos sabemos que no está siempre disponible así que acabarás usando Bash, salvo que tal vez quieras que tu script corra en Alpine y entonces tengas que usar Ash (SH para Busybox), o puede que hasta haya algo peor… 👿

¿Y no habría una forma de escribir scripts de terminal que no implicara elegir entre usar una sintaxis comprensible y mantenible, o tener portabilidad?

Te presento a Plumbum.

Plumbum tiene muchas características interesantes para hacer que escribir scripts se pueda considerar como una práctica mantenible y aceptable dentro de un proyecto de software serio y a largo plazo, sin dejar de ser interesante para cosas rápidas y de corto plazo.

Voy a transformar un script de bash a plumbum para que veas la diferencia (y el parecido).

Primero, en bash:

#!/bin/bash
log WARNING unittest is deprecated and will be removed in Doodba 13.0, \
    use instead: addons update --test $@
# Shortcut to run Odoo in unit testing mode
set -e addons=$1
shift
log INFO Executing Odoo in unittest mode for addons $addons
# HACK `--workers 0`: https://github.com/odoo/odoo/pull/14809
set -x
exec odoo --workers 0 --test-enable --stop-after-init --update "$addons" "$@"

Ahora, en plumbum (python en realidad):

#!/usr/bin/env python3
import logging

from plumbum import FG
from plumbum.cmd import odoo

logging.logging.warn(
    "unittest is deprecated and will be removed in Doodba 13.0,
    "use instead: addons update --test $addons
)
logging.info(
    "Executing Odoo in unittest mode for addons %s",
    sys.argv[0],
)
odoo["--workers", 0, "--test-enable", "--stop-after-init", "--update", *sys.argv] & FG

Mejor, ¿no? La sintaxis de esta biblioteca hace que trabajar con Python sea casi tan cómodo como con bash a la hora de llamar a subprogramas, pero pone todo el potencial de Python a nuestra disposición.

Para que veas las posibilidades de escalabilidad que tiene, vamos a convertir el script anterior a una aplicación CLI de plumbum:

#!/usr/bin/env python3
import logging

from plumbum import FG
from plumbum.cli import Application
from plumbum.cmd import odoo

class UpdateApp(Application):
"""Update odoo addons. This is DEPRECATED."""

    def main(self, addons, *args):
        return odoo["--workers", 0, "--test-enable", "--stop-after-init", "--update", addons, *args] & FG

if **name** == "**main**":
UpdateApp.run()

En estas aplicaciones, los argumentos para el método ˋmain()ˋ son posicionales al ejecutar el comando por CLI, y las opciones tipo ˋ–flag[=valor]ˋ son atributos de la clase.

Como habrás imaginado, si tu aplicación es una clase tan sencilla de declarar, es muy fácil de integrar en cualquier programa.

Incluye también utilidades para:

  • Preguntar algo al usuario.
  • Imprimir mensajes con diversos colores.
  • Ejecutar esta aplicación programáticamente como una clase/método python normal.
  • Conectar a máquinas remotas por SSH y otros protocolos.
  • Gestionar el CWD (local o remoto) en que se ejecuta el programa.
  • Consultar y alterar sus variables de entorno.
  • Lanzar comandos desacoplados del hilo principal.
  • Obtener el STDOUT de un comando y mandarlo como STDIN a otro, redireccionarlo a un archivo, y demá operaciones similares que se pueden hacer con Bash

De forma que la próxima vez que tengas que escribir un script “rápido”, escríbelo en plumbum. Verás que es más sencillo y más mantenible.