Dominando la creación de archivos con "Here-Docs" en BASH.
En el desarrollo de scripts, a menudo necesitaremos crear archivos de configuración XML, JSON, o bien scripts auxiliares que contendrán múltiples líneas de texto. Lo básico es escribir un "echo" tras otro, completando toda la secuencia.
Pero existe un método que creo que es mucho más elegante y bonito, el cual consiste en utilizar un "Here-Document" (o "Here-Doc").
Automatizando la configuración: el poder de "sed".
Imaginemos que necesitamos insertar una configuración específica en el archivo de acciones de Thunar (uca.xml), y que en lugar de escribir el archivo manualmente, utilizamos este bloque de código basado en el comando sed:
sed -i "/<\/actions>/i\
<action>\\
<icon>terminal</icon>\\
<name>ThunExec</name>\\
<command>$terminal_predef</command>\\
<description>Ejecuta en terminal preferida</description>\\
<patterns>*.sh;*.bash;*.zsh;*.fish</patterns>\\
<appears-on-files>true</appears-on-files>\\
</action>" "$thun_uca"
¿Qué hace este bloque?
Es un "inyector" de código. Busca la etiqueta de cierre </actions> dentro del archivo, y justo antes de ella (i), inserta todo el bloque <action>...</action> que configuramos. Es una forma quirúrgica de añadir funciones a un archivo de configuraciones, sin borrar lo que allí ya existía.
- La primera "i" (sed -i...) es una opción del comando sed que significa "in-place", la cual le dice al comando: "no me muestres el resultado por pantalla, editá directamente el archivo original". Si no pusieses esta "i", sed solo imprimiría los cambios en la terminal y el archivo no se modificaría.
- La segunda i (la del medio), es el comando de inserción "insert". En la sintaxis de sed, cuando ponés una barra "/busqueda/i", le estás diciendo al comando: "en la línea en donde encuentres esta búsqueda, insertá lo que siga a continuación, pero justo antes de esa línea".
El desafío: ¿cómo crear el archivo inicial?
Para que el comando "sed" anterior funcione, el archivo "$thun_uca" debe existir previamente y contener al menos las etiquetas básicas. Para lograr esto, tenemos dos caminos.
1. La "millonada de echos vomitantes".
Es la opción que utilizan muchos programadores. Es funcional, pero un poco más difícil de mantener, además de ser menos elegante:
if [[ ! -f "$thun_uca" ]];then
echo '<?xml version="1.0" encoding="UTF-8"?>' > "$thun_uca"
echo '<actions>' >> "$thun_uca"
echo '</actions>' >> "$thun_uca"
fi
Aquí solo hay 3 "echos", pero... ¿podés imaginar 30 o 40 de ellos?
El problema aquí es que cada línea nueva requiere un comando echo y una redirección, lo que convierte un bloque simple en una sucesión de instrucciones que redundan sobre la misma estructura que se repite una y otra vez. Si usáramos el método tradicional, el script se volvería una tarea potencialmente problemática, ya que habría que copiar/pegar líneas "como quien lava y no escurre" o estar repitiendo código de manera enfermiza, como la máquina que NO somos.
2. La forma más elegante: los "Here-Doc"
Esta es la técnica de la que habla este artículo. Es la forma más limpia, porque nos permite definir el "molde" del archivo de una sola vez, manteniendo la estructura XML intacta y legible. Un "Here-Doc" es una forma de decirle a BASH: "tomá todo lo que se escriba a continuación como una única unidad de texto y envialo todo junto al destino". Algo así como el Hypertransport de AMD (enviar más, de un solo golpe) frente al Hyper-Threading de Intel (enviar menos, de a "golpecitos").
if [[ ! -f "$thun_uca" ]];then
cat > "$thun_uca" << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<actions>
</actions>
EOF
fi
Aquí, el bloque <actions></actions> actúa como el contenedor necesario. Una vez que este bloque crea el archivo base, el comando sed que vimos antes entrará en juego para insertar la acción "ThunExec" justo en medio de esas dos etiquetas.
¿Cómo interpretar este código?
cat > "$thun_uca": El comando cat toma lo que recibe y lo escribe en el archivo indicado por la variable $thun_uca. El símbolo > asegura que, si el archivo existe, se sobrescriba.
<< 'EOF': acá ocurre la magia. Las llaves << inician el Here-Doc. La palabra EOF es simplemente un marcador que elegimos nosotros (podría ser cualquier palabra, por ejemplo "POCHO", pero EOF —End Of File— es el estándar).
Importante: al envolver EOF en comillas simples ('EOF'), le estamos dando una instrucción vital al shell: "no interpretes nada de lo que hay adentro, sé literal". Esto evita que BASH busque variables con $ para expandirlas o intente ejecutar comandos dentro del texto, en lugar de tratar a todo como texto plano.
El final del bloque: el EOF que aparece al final, solo y sin sangrías, le avisa a BASH: "hasta acá llegó el archivo".
Reglas de oro para estudiantes.
El delimitador final debe estar solo: nunca pongas espacios o tabulaciones antes del EOF de cierre, porque si lo hacés, BASH no reconocerá el fin del bloque y el script se quedará "colgado" esperando el cierre.
Literalidad: gracias a las comillas simples ('EOF'), podés copiar y pegar cualquier fragmento de código (HTML, XML, JSON, Python) dentro del bloque sin miedo a que los caracteres especiales (como $ o ") rompan el flujo de tu script.
Orden: esta técnica es la forma más "limpia" de gestionar configuraciones. Mantiene el contenido del archivo de destino visualmente idéntico a cómo debería quedar en el medio de almacenamiento, facilitando mucho la edición futura.
Nota importante sobre la sangría (indentación, tabulación) dentro del "Here-Doc".
Indentar, tabular, sangrar el texto al programar es "mandatory!", al igual que comentar lo no obvio.
Es posible que al escribir tu código dentro del Here-Doc quieras usar tabulaciones o espacios para que el XML se vea ordenado. Podés hacerlo sin problemas, pero con una advertencia: el delimitador de cierre (el EOF, el POCHO) debe estar pegado contra el margen izquierdo de la pantalla. Si ponés espacios antes del EOF final, BASH pensará que esos espacios son parte del contenido que debe buscar, no los encontrará, y tu script se quedará "colgado" esperando el cierre.
El truco avanzado: si necesitás que el código dentro del bloque también tenga sangría visual y que el EOF final pueda estar alineado con ella, podés usar <<-EOF (agregando un guión después de los signos "menor que"). Ese mismo guión será el que le indique a BASH que ignore todas las tabulaciones iniciales de cada línea.
Así es cómo quedaría el código, aplicando el truco del guión:
if [[ ! -f "$thun_uca" ]];then
cat > "$thun_uca" <<- 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<actions>
</actions>
EOF
fi
Como podés ver, tanto el bloque interior como el EOF de cierre pueden estar perfectamente indentados, y BASH, gracias a ese guión, ignorará esas tabulaciones iniciales al procesar el archivo. Esto hace que el código quede en consonancia con los "case", "if", etc, que ya poseas en tu script.
NOTA EXTREMADAMENTE IMPORTANTE: para que esto funcione, la sangría dentro del bloque debe
hacerse con tabulaciones reales ([Tab]) y no con espacios ([Space]), ya que <<- está
diseñado específicamente para ignorar caracteres de tabulación, no "espacios puestos con la barra espaciadora", al
inicio de cada línea incluida en el bloque.

