2009-12-23 15:32

Con iterpipes, python puede reemplacar a bash para scripting. En serio.

Esto me molesta hace mucho: programar scripts de shell es horrible. Son feos y llenos de errores. ¿El único motivo por el cual se sigue haciendo? Porque no hay alternativa.

O al menos así era hasta que hoy me encontré con iterpipes en python.reddit.com

Iterpipes es "una biblioteca para usar pipelines de shell usando sintaxis parecida al shell" y ¿sabés qué? Es brillante.

Acá hay un ejemplo de su página de PYPI:

# Total lines in *.py files under /path/to/dir,
# use safe shell parameters formatting:

>>> total = cmd(
...     'find {} -name {} -print0 | xargs -0 wc -l | tail -1 | awk {}',
...     '/path/to/dir', '\*.py', '{print $1}')
>>> run(total | strip() | join | int)
315

Así sería en shell:

find /path/to/dir -name '*.py' -print0 | xargs -0 wc -l | tail -1 | awk '{print $1}'

Tal vez digas que la versión de shell es más sencilla. Eso es una ilusión causada por la densidad del campo malico del shell: esa versión tiene bugs.

Porqué tiene bugs? Porque si controlo qué hay adentro de /path/to/dir puedo hacer que falle [1], pero en python puedo manejar el error.

También en la mayoría de los intentos de escribir ese comando sería inseguro porque las reglas de comillas y caracteres de escape de shell son dementes.

La versión de iterpipes usa el equivalente de prepared statements de SQL que debería ser más seguro.

Es casi imposible hacer ese comando en puro shell y estar seguro de que es seguro.

Además la versión en shell produce un string en vez de un entero, que es una bazofia si lo necesitás usar para algo.

Y el beneficio más importante es, por supuesto, no cuando tratás de hacer que python funcione como un shell, sino cuando podés dejar de hacer como que el shell es un lenguaje de verdad.

Comsideremos esta gema del script /etc/rc.shutdown en Arch Linux. DAEMONS es una lista de cosas que se arrancaron al inicio, y este script intenta detenerlas en orden inverso, a menos que el nombre empiece con "!":

# Shutdown daemons in reverse order
let i=${#DAEMONS[@]}-1
while [ $i -ge 0 ]; do
        if [ "${DAEMONS[$i]:0:1}" != '!' ]; then
                ck_daemon ${DAEMONS[$i]#@} || stop_daemon ${DAEMONS[$i]#@}
        fi
        let i=i-1
done

¿No es lindo?

¿Y cómo se vería en python? (tal vez haya invertido el significado de ck_daemon):

# Shutdown daemons in reverse order
for daemon in reversed(DAEMONS):
    if daemon[0]=='!':
        continue
    if ck_daemon(daemon):
        stop_daemon(daemon)

Mientras que stop_daemon solía ser esto:

stop_daemon() {
    /etc/rc.d/$1 stop
}

Ahora será esto:

.. code-block:: python
def stop_daemon(daemon):
run(cmd('/etc/rc.d/{} stop',daemon))

Vamos gente, estamos en pleno siglo 21, y el shell scripting ya era feo en el 20.

[1] Ejercicio para el lector.

Comentarios

Comments powered by Disqus

Contents © 2000-2019 Roberto Alsina