28 de Febrero, 2010

'Heredoc' y la sustitución de variables en BASH

A principios de esta semana estaba copiando del clipboard a vim, sobre una sesión remota de SSH, un trozo de código copiado de una web, y el editor insistía en aplicarme un sangrado absurdo que lo descomponía todo, así que en lugar de pelearme con la configuración (no acertaba con el set o unset adecuado, y no sé porqué la ayuda de vim no estaba instalada), se me ocurrió usar un here document para salir del paso (algo así como documentos empotrados; no me gusta la traducción literal de la Wikipedia).

Lo fácil que hubiera sido copiar el fichero a disco localmente y usar scp(1), ¿verdad? Pero gracias a esa forma, un tanto rara a veces, en la que funciona nuestro cerebro, aprendí una cosa nueva :).

Básicamente los here document (o heredoc), son una funcionalidad que da el shell (y que han heredado algunos lenguajes de programación de alto nivel) que nos permite incluir un documento dentro del propio programa. El operador del shell es <<tag:

#!/bin/sh

cat <<FINDOC
Esto es un heredoc (o documento empotrado) que
hemos incluido en este script.
FINDOC

En ese ejemplo cat(1) mostrará por pantalla todo lo que encuentre hasta llegar a la marca FINDOC.

Esto es muy útil cuando queremos generar contenido y resulta incómodo poner múltiples lineas con echo(1) o recurrir a un fichero externo.

En mi caso usé el própio intérprete interactivo para pegar el código, que era un script en shell precisamente. Usé algo así como:

$ cat >script.sh <<FIN
#!/bin/sh

ejemplo="hola mundo"
echo mensaje $ejemplo

FIN

¡Bien! Asunto resuelto y a otra cosa; salvo por el detalle de que el shell interpreta las variables, con lo que el fichero resultante no era el esperado :S.

Entonces el problema pasó a ser el propio heredoc (aquello de que si decides usar X para resolver un problema, tienes dos problemas :D). De hecho es curioso como te olvidas incluso que tu objetivo era realmente copiar un fichero de un sitio a otro ;).

Investigando un poco aprendí algo que no sabía: si ponemos el delimitador entre comillas, ¡no se interpretan las variables!

$ cat >script-2.sh <<"FIN"
#!/bin/sh

ejemplo="hola mundo"
echo mensaje $ejemplo

FIN

Podemos comprobarlo con nuestro ejemplo:

$ diff -u script.sh script-2.sh
--- script.sh	2010-02-28 19:20:07.755883961 +0000
+++ script-2.sh	2010-02-28 19:20:13.824883877 +0000
@@ -1,5 +1,5 @@
 #!/bin/sh
 
 ejemplo="hola mundo"
-echo mensaje 
+echo mensaje $ejemplo
 

La otra opción, que sí conocía, era la de escapar todas las variables y posibles patrones de sustitución ($ejemplo pasa a ser \$ejemplo), pero era poco práctico porque el script era bastante largo.

Así que nunca es tarde para aprender cosas nuevas, aunque sea debido a que no usamos la herramienta adecuada para resolver el problema ;).

Actualización: como bonus, the geek joke of the week (my cat is escaped :'D).

Anotación por Juan J. Martínez, clasificada en: script, bash.

Hay 3 comentarios

Gravatar

Para lo del paste en vim, en modo comando:

:set paste

ahora en modo edición podrás pastear respetando el formato original del paste.

por funky, en 2010-03-01 07:52:33

Gravatar

Seguimos aprendiendo cosas nuevas :D

Gracias!

por Juanjo, en 2010-03-01 07:56:31

Gravatar

yo el problema de vi, que no ocurría con gvim lo solucione como indica funky y lo averigüé en :
http://sherekan.com.ar/2009/10/15/vim-en-tu-firefox-con-vimperator/

por nell, en 2010-03-03 17:53:48

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: