Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

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

O al me­nos así era has­ta que hoy me en­contré con iter­pi­pes en py­tho­n.­re­ddi­t.­com

Iter­pi­pes es "u­na bi­blio­te­ca pa­ra usar pi­pe­li­nes de she­ll usan­do sin­ta­xis pa­re­ci­da al she­ll" y ¿sa­bés qué? Es bri­llan­te.

Acá hay un ejem­plo de su pá­gi­na de PY­PI:

# 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)

Así se­ría en she­ll:

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

Tal vez di­gas que la ver­sión de she­ll es más sen­ci­lla. Eso es una ilu­sión cau­sa­da por la den­si­dad del cam­po ma­li­co del she­ll: esa ver­sión tie­ne 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.

Tam­bién en la ma­yo­ría de los in­ten­tos de es­cri­bir ese co­man­do se­ría in­se­gu­ro por­que las re­glas de co­mi­llas y ca­rac­te­res de es­ca­pe de she­ll son de­men­tes.

La ver­sión de iter­pi­pes usa el equi­va­len­te de pre­pa­red sta­te­men­ts de SQL que de­be­ría ser más se­gu­ro.

Es ca­si im­po­si­ble ha­cer ese co­man­do en pu­ro she­ll y es­tar se­gu­ro de que es se­gu­ro.

Ade­más la ver­sión en she­ll pro­du­ce un string en vez de un en­te­ro, que es una ba­zo­fia si lo ne­ce­si­tás usar pa­ra al­go.

Y el be­ne­fi­cio más im­por­tan­te es, por su­pues­to, no cuan­do tra­tás de ha­cer que py­thon fun­cio­ne co­mo un she­ll, sino cuan­do po­dés de­jar de ha­cer co­mo que el she­ll es un len­gua­je de ver­da­d.

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]#@}
        let i=i-1

¿No es lin­do?

¿Y có­mo se ve­ría en py­tho­n? (tal vez ha­ya in­ver­ti­do el sig­ni­fi­ca­do de ck_­dae­mo­n):

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

Mien­tras que sto­p_­dae­mon so­lía ser es­to:

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

Aho­ra se­rá es­to:

.. code-block:: python
def stop_daemon(daemon):

run(­cm­d('/e­tc/r­c.­d/{} sto­p',­dae­mo­n))

Va­mos gen­te, es­ta­mos en pleno si­glo 21, y el she­ll scrip­ting ya era feo en el 20.


Ejer­ci­cio pa­ra el lec­to­r.

tzulberti / 2009-12-23 20:21:

definitivamente te la pasas revisando pipy.....

Roberto Alsina / 2009-12-23 20:30:

No, estoy subscripto a ... mucho menos laburo! ;-)

Dimitris Leventeas / 2009-12-23 22:22:

Excellent post!
Bash is obsolete. Long live Python!

elpargo / 2009-12-24 08:02:

Awesome thank you!

elpargo / 2009-12-24 08:02:

Just today I was ranting about bash and how it sucks so much!

Eric O LEBIGOT (EOL) / 2009-12-24 09:43:

Thank you for sharing.

IPython is also great for on-the-fly system administration:

>>> files = !ls *.py

Andrey Vlasovskikh / 2009-12-25 08:26:

Thanks for your review :) The library is still in active development and definitely requires more documentation, testing, cleaning up its API, etc.

Your example with Arch Linux init scripts is quite interesting, because scripting in Arch is one of my use cases for the library.

BTW, in run(cmd('/etc/rc.d/{} stop',daemon)), you should use call or check_call instead of run as run returns Iterable -> Iterable and here you just need to force immediate execution of a command. As I've already said, more docs are needed.

Roberto Alsina / 2010-01-14 12:58:

Thanks for the correction. That's what happens when I don't test the code I post!

Kenny Meyer / 2009-12-27 14:15:

I'm a Python lover and this post is a WOW! experience. Python scripts look **so much** better than Bash scripts do!

Also the *error handling* is a plus point of why to replace a dynamic programming language, like Python, with an old-school scripting language like Bash.

Maybe this was a tedious task when using the `sys` and `os` or `subprocess` module for executing commands and let's not talk about pipelining... that was very ugly. But with `iterpipes` this is a complete new experience.

Thanks for sharing!

Roberto Alsina / 2010-01-14 12:57:

There is a nice, long discussion of this post here (in czech):

Google translation:

Joan / 2010-02-14 10:18:

Hi! You could be interested on Scripy. It has been built to replace to bash scripts, it's integrated with logging (all shell commands can be logged), to lets edit files (more easy than in bash), and more...

Roberto Alsina / 2010-02-14 12:22:

Very interesting!

Chris2048 / 2011-04-13 02:07:

In your first example you use pipes in an infix notation; I can't find that iterpipes supports this, was it dropped?

phone number lookup / 2011-12-03 22:30:

this is really interesting viewpoint on the subject i might add

employment background check / 2011-12-27 23:30:

Man ... Beautiful . Amazing ... I will bookmark your website and use the your RSS feed also

cell phone lookup / 2012-01-17 05:52:

Your blog has the same post as another author but i like your better