LVM: la gran mentira corporativa del almacenamiento (o por qué tus medios de almacenamiento no son piezas de Lego)


LVM: la gran mentira corporativa del almacenamiento (o por qué tus medios de almacenamiento no son piezas de Lego).

En Entropía binaria siempre defendimos a rajatabla la filosofía KISS (Keep It Simple, Stupid) y la soberanía sobre nuestros sistemas. Creemos en el software que hace una sola cosa y que la hace bien, y en mantener el control absoluto sobre los fierros. Pero de vez en cuando, la industria nos quiere vender espejitos de colores, empaquetando complejidad innecesaria bajo la promesa de la "comodidad". Hoy vamos a hablar de una de las peores basuras corporativas que se han estandarizado en el mundo Linux: el Logical Volume Manager, o LVM.

Te lo venden en todos los manuales y cursos genéricos como la solución definitiva. Te dicen que con LVM administrar discos es como jugar con piezas de Lego: podés poner y sacar unidades en caliente, expandir el espacio mágicamente y ser feliz.
Lo piden en "LinkedIn" y muchas veces puede llegar a ser bueno incluirlo en tu CV, solo para agarrar ese laburo.
Pero es una profunda y absoluta mentira.

El ADN de LVM.

Al código original de LVM lo escribió un alemán llamado Heinz Mauelshagen en 1998. El tipo trabajaba para una empresa llamada Sistina Software. ¿Y de dónde sacó la inspiración? Copió el diseño del administrador de volúmenes de HP-UX (el pesado UNIX propietario de Hewlett-Packard), que a su vez estaba basado en el sistema de Veritas. Desde su ADN, es tecnología pensada para armarios llenos de discos en datacenters de los años 90.
Como a Red Hat no solo produce este tipo de tecnologías basura sino que también le interesa adquirirlas cuando no las pueden crear en casa, la necesitaban para vender soporte caro a los bancos (que tenían servidores de base de datos gigantes), y en 2003 terminan comprando Sistina Software. A partir de ahí, Red Hat adoptó a LVM como su hijo pródigo, lo estandarizó (lo metieron por defecto en los instaladores de RHEL y Fedora) y financiaron el desarrollo de LVM2. Por eso, hoy en día, LVM es sinónimo del ecosistema Red Hat/SUSE y su filosofía de "venderte la cura para la enfermedad de complejidad que ellos mismos te encajaron de prepo", solo porque a ellos les sirve, no a vos, como siempre.

Mi experiencia en el barro: preso en mi propio sistema.

Hablo desde la experiencia cruda, porque cometí el error de probarlo en mi instalación diaria hace tiempo, cuando todavía usaba openSUSE, que lo ofrece en su instalador como "una opción más a considerar". El resultado fue catastrófico; quedé más preso y atado de manos que si hubiera comprado un producto de Apple. No podía zafar de ninguna manera de ese ecosistema sórdido.

El mito del "Lego" se me desmoronó en la cara el día que necesité desconectar físicamente un disco duro secundario. Era una unidad que solo contenía datos intrascendentes, archivos sueltos. Nada del sistema operativo estaba ahí. Tiré del cable SATA y, al encender la máquina, se me jodió (sí, no hay otra palabra posible, creeme) todo el sistema LVM entero. El núcleo entró en pánico y la máquina se negó a arrancar.
¿Por qué? Porque para esta tecnología de mierda (no hay otra palabra posible), vos no tenés discos independientes. Tenés una "bolsa negra", una volqueta con tapa. Si le sacás un bloque a esa bolsa, aunque sea de datos inútiles, el gestor asume que toda la estructura está corrupta. No opera en caliente un carajo; si no le avisás por terminal con comandos rebuscados que vas a retirar un volumen, te destruye el acceso al resto.

Y ni se te ocurra intentar formatear una de esas particiones lógicas por tu cuenta. Al hacerlo, le cambiás el UUID a la partición. La capa de LVM pierde el rastro, el fstab enloquece y, de nuevo, te cargás todo el arranque del sistema. Es una trampa mortal diseñada para alejarte del control real de tu máquina.
Humo corporativo: ¿quién usa realmente esto?
Nos repiten que es un "estándar de la industria". Pero hagámonos una pregunta seria: ¿Google, Netflix, Spotify o Anthropic usan LVM en sus servidores de producción masiva?
La respuesta es un rotundo "no".

Esos gigantes tecnológicos no pierden el tiempo con un administrador redimensionando volúmenes lógicos a las tres de la mañana. Operan con sistemas de archivos distribuidos y tratan a los servidores como tristemente se trata al ganado. Si un disco se llena o falla, el nodo se descarta, el tráfico se redirige y se levanta una máquina nueva. LVM ahí es puro ruido, una capa de abstracción inútil que solo agrega puntos de fallo.
LVM es, en esencia, tecnología heredada para infraestructuras corporativas monolíticas y dinosaurios informáticos. Está pensado para ese viejo servidor Red Hat en el sótano de un banco que corre una base de datos gigante que no se puede apagar nunca, o para tapar la pésima planificación de hardware de un administrador que necesita meter tres discos nuevos a un NAS departamental sin cortar el servicio. Para tu PC de escritorio, para tu notebook o para un servidor web limpio, es un estorbo. Es el ejemplo perfecto de una NTI (Nueva Tecnología Innecesaria).

Conociendo al enemigo: cómo funciona la trampa.

Yo fui librero durante 15 años. Llegué a odiar a autores como Osho y Paulo Coelho, y a muchos otros que ni siquiera operaban en ese nivel, lucrando con las miserias de la gente de a pie. Una vez, una clienta me dijo "si no lo leíste no podés criticarlo solo por la "pinta" o por las contratapas de sus libros". Más allá de que ese es un error bastante importante, porque vos podés informarte sobre esdta clase de fantasma literario por artículos y charlas que dan escritores de verdad, leí, muy a mi pesar y finalmente "El alquimista". ¿Te imaginás lo que pasó cuando la clienta volvió?

Para odiar algo con fundamentos técnicos, primero hay que entender cómo funciona.
El problema central de LVM es que destruye la relación directa entre el sistema operativo y el metal, inyectando tres pesadas capas de abstracción.

Vamos a desarmar esta arquitectura "de arriba hacia abajo".

1. PV (Physical Volume - Volumen físico).

Esta es la capa base, el metal real. Un PV puede ser un disco duro entero (/dev/sdb) o una partición de hardware clásica (/dev/sda2). En un sistema limpio, acá es en donde formateás y guardás tus datos. Pero en LVM, al hardware se lo "marca" y se lo vacía de identidad para entregárselo al gestor. El disco deja de ser un disco para convertirse en un simple ladrillo ciego.

2. VG (Volume Group - Grupo de volumen).

Acá es en donde ocurre el desastre lógico. LVM agarra todos esos "ladrillos" (los PVs) y los mete adentro de una gran "caja de fierro", fusionándolos. Este es el VG. Si metiste un disco de 500GB y otro de 1TB, el VG le dice al sistema: "hola, soy un gran tanque de 1.5TB".
El problema mortal reside en que los datos se esparcen por este tanque de forma abstracta. Por eso, cuando yo saqué mi disco de datos en openSUSE, el VG sintió que le habían arrancado un pedazo de su propia carne y bloqueó toda la estructura.

3. LV (Logical Volume - Volumen lógico).

Como el sistema operativo no puede instalarse en una "volqueta", LVM tiene que crear particiones virtuales para engañarlo. Esos son los LVs. Dentro de tu gran Grupo de Volumen, vos recortás pedazos de espacio virtual y les ponés nombre: un LV para la raíz (/), un LV para la swap, otro para el /home.
A los ojos de tu sistema Linux, estos LVs parecen particiones reales y recién a ellos se les da un formato (ext4, xfs). Pero es una ilusión; por debajo, están fragmentados y dependientes de esa gran "bolsa frágil" que es el VG.

Conclusión para administradores de verdad.

La soberanía digital empieza por entender exactamente dónde están guardados nuestros bits. Cada capa de abstracción que metemos entre el software y el hardware es un lugar oscuro donde los problemas se esconden y se multiplican.
Si querés un sistema robusto, rápido y del que tengas el control absoluto, mantenete alejado de LVM. Usá lsblk para mirar el mapa, cfdisk para esculpir el metal con precisión, dale un formato directo y montalo limpio en tu fstab mediante su UUID. Esa es la verdadera forma UNIX. Todo lo demás, es humo corporativo. O, por qué no, mierda corporativa.


Aprendemos a usar grep en el aula: guía breve pero fundamental.



Aprendemos a usar grep en el aula: guía breve pero fundamental.



grep como legado UNIX puro.


Antes de entrar en opciones y ejemplos, nos vamos a detener tan solo un minuto en lo que representa grep dentro del ecosistema UNIX.
No es solo una herramienta para buscar texto, sino una pieza clave, un importante legado respecto a una idea mucho más grande que el comando en sí... Un concepto inherente a esta orden que debería estar recordándonos permanentemente cómo programar bien.
Dentro del ecosistema UNIX, los programas bien hechos no están pensados para hacerlo todo, sino para hacer una sola cosa bien (principio KISS) y para poder combinarse entre sí. Esta combinación se logra a través de las tuberías (pipes), en las cuales la salida de un comando (o programa), se convierte en la entrada de otro.
grep encarna ese principio de forma excelente, porque no necesita saber de dónde vienen los datos ni qué se hará con ellos después. Simplemente recibe texto, filtra según un criterio y devuelve el resultado. Esta simplicidad es lo que le permite integrarse en flujos más complejos: filtrar el "ruido innecesario" en la salida de otros comandos, analizar archivos de "logs" en tiempo real, procesar listados generados por otros comandos o inspeccionar grandes volúmenes de información sin necesidad de herramientas pesadas.
En este sentido, utilizar grep es empezar a pensar con cabeza UNIXera, en donde construir soluciones a partir de piezas pequeñas encadenadas con absoluta precisión, es... mandatory!
Es una forma de trabajar en la cual la verdadera potencia no radica en un comando o programa individual, sino en la manera en que estos se conectan entre sí.


Prefacio.

grep es una de esas herramientas que aplicada de manera directa en la terminal puede parecer simple, pero que rápidamente se va volviendo indispensable cuando se empieza a trabajar con archivos de "logs" o directamente con código. En esencia, hace algo muy concreto: buscar texto. Sin embargo, la forma en la cual lo hace (tanto como también las opciones que ofrece) son determinantes al utilizarlo como filtro básico o como potente herramienta de diagnóstico.


No me importa cómo esté escrito: grep -i

Una de las primeras dificultades que podemos encontrar, es que el texto no siempre aparece exactamente como lo esperamos... o como lo recordamos.
El mismo puede estar en mayúsculas, en minúsculas, o en una combinación de ambos casos.
Por ejemplo, yo mismo, ayer, creé una función llamada "obtener_noticias_openbsd" y la estaba invocando como "obtener_noticias_OpenBSD"... Resultado: error de ejecución en el flujo esperado del programa.
Es el eterno dilema de escribir las cosas como realmente se debe, por un lado, vs lo que se recomienda en programación, por otro (sumale a eso "año vs anio", "función_principal vs funcion_principal", y así).
Acá es donde entra en juego JuanI Vellozo, quien presentó este ejemplo: el de la opción "-i". Al utilizarla, grep deja de distinguir entre mayúsculas y minúsculas (deja de lado el "case sensitive"), lo cual permite encontrar coincidencias sin la preocupación de cómo fue escrito el texto. Esta es una mejora procedimental que evita muchos "falsos negativos" cuando se están buscando palabras clave o comandos problemáticos dentro del código.

Ejemplo.

Supongamos que existe un archivo llamado "log.txt" con un contenido como este:

[001] Error al iniciar módulo: no se encuentra el archivo o directorio.
[002] OK.
[003] OK.
[004] Conexión no establecida
[005] OK.
[006] OK.
[007] ERROR: módulo de conexión faltante.
[008] Usuario no conectado.
[009] OK.
[010] OK.
[011] Saliendo con código de error 1.

Al hacer

grep -i "error" log.txt

la terminal nos devuolverá tanto "Error", como "ERROR" y "error", resolviendo el problema de las mayúsculas sin tener que "debuguear" demasiado.


Pero... ¿en dónde está el error, exactamente? grep -n

Cuando el archivo crece o se empieza a volver "inmanejable", encontrar una coincidencia ya no es suficiente: también hay que ubicarla.
Gracias a Ramiro Bergara, vimos la demostración de que la opción -n descubre este contexto que en muchas ocasiones es fundamental, mostrando el número de línea junto a cada resultado.
Esto potencia a la utilidad de búsqueda de grep, porque permite saltar directamente al lugar exacto dentro del archivo en donde está el error o lo buscado. En tareas de depuración o edición, ese número deja de ser un simple detalle, pasando a ser una guía importante.

Ejemplo.

grep -n "error" log.txt

El resultado incluirá tanto las líneas como sus números reales, algo así como:

  1:[001] Error al iniciar módulo: no se encuentra el archivo o directorio.
  7:[007] ERROR: módulo de conexión faltante.
11:[011] Saliendo con código de error 1.

Esto permite ubicar rápidamente al problema.


¿Voy a tener que hacer esto en cada uno de mis programas? grep -r

Hay un gran salto que es posible dar cuando no estamos obligados a parametrar nuestras búsquedas dentro de un solo archivo. En el caso de un programador que posee una cantidad de programas en su haber, la información, lógicamente, está distribuida en múltiples archivos y directorios. Thiago González mostró que grep puede recorrer directorios y archivos enteros de manera recursiva, buscando en todo lo que a su paso encuentra. Esto hace que la necesidad de tener que saber de antemano en dónde está lo que buscamos, sea algo totalmente irrelevante, ya que en lugar de ir abriendo todos los archivos uno por uno y aplicando herramientas de búsqueda en cada uno de ellos, se delega la totalidad de dicha tarea a este comando, el cual lo hará de manera inmediata y efectiva.

Ejemplo.

Este es un volcado real de mi directorio de scripts local, el cual busca la ocurrencia "set -e" en "$PWD", s decir "en el lugar en donde esté 'parada' la terminal en ese momento".

grep -r "set -e" $PWD

/mnt/HDD_4TB/datos_linux/01_Entropía/CodeBerg/_GeneBerg/_respaldos/geneberg.sh:set -euo pipefail
/mnt/HDD_4TB/datos_linux/01_Entropía/CodeBerg/_GeneBerg/geneberg.sh:set -euo pipefail
/mnt/HDD_4TB/datos_linux/01_Entropía/CodeBerg/KDEnTux/KDEnTux.sh:set -e # Detención script inmediatamente si algún comando falla.
/mnt/HDD_4TB/datos_linux/01_Entropía/CodeBerg/KDEnTux/KDEnTux.sh:    # Se utiliza "vainfo" para listar puntos de entrada VA-API. Si falla, KDEnTux se detendrá gracias al "set -e" del inicio.
/mnt/HDD_4TB/datos_linux/01_Entropía/CodeBerg/GeneRead/generead.sh:set -euo pipefail 
/mnt/HDD_4TB/datos_linux/01_Entropía/CodeBerg/IA_local/instalador_ia_local.sh:set -euo pipefail


Pero... yo quiero ver "todo lo otro", no "eso"... grep -v

A este atributo de grep, creo que lo charlamos entre todos, y tiene que ver con otra situación frecuente, que es la de no siempre necesitar ver lo que coincide, sino lo inverso.
Me explico mejor: a veces lo que interesa es limpiar un archivo, eliminar líneas irrelevantes o quedarse con lo que no cumple con cierta condición.
Para eso existe "-v", que invierte el criterio de búsqueda: en lugar de mostrar coincidencias, muestra todo lo demás, es decir, todo lo que NO coincide con lo que le estemos indicando.
Esto es especialmente útil al ponernos a trabajar con archivos de configuración o de registros en donde el filtrado de "ruido" es imprescindible (comentarios, líneas repetitivas, etc).

Ejemplo.

Dado un archivo de configuración con comentarios:

# Configuración del sistema
usuario=admin
# Contraseña
password_SHA-256=p989e5ap9h7t6894d4topja34l5l69xq

Podemos quitar los comentarios con:

grep -v "^#" config.conf

El resultado será solo las líneas útiles, sin las que empiezan con #:

usuario=admin
password_SHA-256=p989e5ap9h7t6894d4topja34l5l69xq


Un dato "de color": grep --color=auto

Por último, está el aspecto visual, que muchas veces se subestima. Cuando se trabaja con grandes volúmenes de texto, encontrar rápidamente coincidencias dentro de cada línea puede ser más dificultoso de lo que parece, y más cuando llevamos mirando el mismo código durante horas.
La opción --color=auto resuelve esto resaltando las coincidencias directamente en la salida. No cambia el resultado en sí, pero sí la forma en que lo percibimos, haciendo que la lectura sea más inmediata y menos propensa a errores.

Ejemplo.

Volvamos al caso del ejemplo en grep -r.
Era este: grep -r "set -e" $PWD
Pero supongamos que queremos resaltar visualmente a las ocurrencias encontradas, no solamente volcarlas en pantalla.
En ese caso, podemos simplemente intercalar la opción correspondiente en la misma línea, de esta manera:

grep --color=auto -r "set -e" $PWD

El resultado, será el mismo que el mostrado en la imagen de este artículo.

P.D.: para que no te rompas la cabeza intentándolo... Porque te estoy leyendo el pensamiento y ya sé que vas a querer hacer lo mismo que yo intenté en algún momento ("color=1", "color1", etc...), y te quiero ahorrar tiempo y enseñarte algo más...

  • "--color" no acepta colores, solo acepta "auto", "always" y "never" (el cual ya viene por omisión y no es necesario, en principio).
  • "color=auto", colorea solo si detecta que la salida va a una terminal o pantalla, pero si detecta que la salida es redirigida a un archivo, deja de resaltar con color automáticamente.
  • "color=always" se comporta de manera literal: inserta comandos de color aunque eso pueda llegar a ensuciar su salida.
Este modo se vuelve útil en casos específicos, por ejemplo cuando querés forzar colorido a través de una tubería hacia otro comando o programa que sí sepa interpretar código ANSI, el cual es, en definitiva, el código generado por grep cuando está en modo "color=always".


Epílogo.

grep posee muchas opciones, y de hecho te invito a que las investigues por tu cuenta, pero éstas, utilizadas de manera combinada (tal como nos enseña UNIX), transforman a cualquier comando (en este caso, a grep) en algo más que una simple orden o un simple buscador de texto: permiten explorar archivos, localizar problemas y filtrar información con precisión. 
Esta guía fue escrita para mis estudiantes y para los lectores que la aprovechen, pero también para mí mismo, porque al bajar a tierra escribiendo lo que uno aprendió, lo "hace carne".
Para alguien que esté empezando a utilizar este comando, la moraleja es que no es tan importante tratar de memorizar todas las posibilidades como entender que cada una de estas variantes responde a una necesidad concreta que irá apareciendo naturalmente al trabajar con datos reales en BASH.


¿Cómo ejecutar con "sudo" cuando el usuario "is not in the sudoers file".

Hoy tuve que hacer un malabar en un sistema Mageia de la UTU que yo mismo administro.
Los sistemas Linux que pasan por mí, son endurecidos a un nivel bastante paranoico, y una de las primeras cosas que hago, es quitar a los usuarios del grupo "wheel" para que no puedan hacer "sudo"; o sos root, o no sos root: nada de filosofía Linux, acá lo que corre es filosofía UNIX.
Lo que esto significa, es que si no sabés la contraseña de root y hacés sudo, no podrás seguir adelante porque el sistema, lo que te pedirá, será tu contraseña (un contrasentido total heredado del mundo de fantasía en el que está viviendo Linux desde hace muchos años) y no la de root.
Observá los 2 ejemplos en las siguientes 2 imágenes. ¿Te ha sucedido algo así?


Pues bien, hay veces en las cuales hacer "su" y registrarse como root en la terminal (aunque poseas la contraseña de root) no es suficiente, ya que la ejecución de un programa o script puede necesitar "los privilegios de sudo pero en el contexto del usuario que inició sesión en el sistema". Por ejemplo, si el usuario es "estudiante" y el script tiene que correr para "estudiante", no podés hacer "su" en la terminal y "pasarte a sudo completamente", porque el contexto del usuario "estudiante" se perdería.
Tampoco podés hacer "sudo ALGO" siendo usuario "estudiante sin wheel" porque te pasaría lo que se muestra en las imágenes de arriba.

Pues, en Linux hay solución para casi todo, y esta no es la excepción.

Podés agregar a cualquier usuario al grupo wheel, o al archivo de sudoers.
Pero esto rompería la filosofía UNIX en donde "root es root" y "user es user"

Podés "pasarte a root", como dijimos antes, pero eso rompería el contexto del usuario que realmente necesita correr el script "para él y no para root".

Entonces... parecería que no hay solución... ¿o sí?

Estarás pensando... "Páh... Soy yo el administrador; soy yo el que prohíbe hacer "sudo". En mis equipos, si no sabés la contraseña root, no podés elevar privilegios usando tu contraseña... Tiene que haber manera. Soy el "admin", sé la contraseña de root, solo debo hacer que el sistema lo entienda sin reconfigurar nada, haciendo algo al vuelo... 

Esto es posible. La "línea salvadora" es esta:

su -c "SUDO_USER=<NOMBRE_USUARIO> bash /ruta_al_script" root

Esto no solamente soluciona el problema, sino que también ejecuta al script como root, pero con SUDO_USER configurado para el usuario real, procedimiento que será utilizado por el script para saber a quién pertenece el entorno: solo tenés que sustituir <NOMBRE_USUARIO> por el nombre del usuario real.

Ejemplo con el usuario "estudiante":
su -c "SUDO_USER=estudiante bash /ruta_al_script" root

Dominando la creación de archivos con "Here-Documents" (here-docs) en BASH


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.

De la estructura clásica a la potencia del "cortocircuito" en BASH


De la estructura clásica a la potencia del "cortocircuito" en BASH.


En BASH, existen muchas formas de tomar decisiones.
En este artículo, vamos a hacer un breve "viaje" desde la estructura más básica del "if" hasta una técnica avanzada y compacta que verás en los scripts que más experiencia acumulada encierran: el operador de "cortocircuito".

1. Punto de partida: el "if" básico.


Todo estudiante comienza por aquí. Es la forma más legible y segura de ejecutar algo si se necesita verificar el cumplimiento de una condición.

# Si el archivo existe, notificarlo.
if [[ -f "$archivo" ]];then
    echo "El archivo existe."
fi


2. Agregando alternativas: "else" y "elif".

En cuanto el script comienza a crecer, es necesario empezar a manejar el "qué pasa si no", y aquí es en donde entran en escena los actores "else" (si no) y "elif" (y si).

if [[ -f "$archivo" ]];then
    echo "Es un archivo regular."
elif [[ -d "$archivo" ]];then
    echo "Es un directorio."
else
    echo "No se encontró nada con ese nombre."
fi


3. El salto al minimalismo: operadores && y { }

A veces, podemos juzgar que un bloque "if" de 5 líneas para una tarea muy pequeña está ocupando "demasiado espacio visual", y es este tipo de situación el origen del código compacto, utilizando, entre otras herramientas, al operador "&&" (AND).

En BASH, el "&&" le dice al sistema: "ejecutá lo que sigue SOLO SI la operación anterior terminó con éxito".

Si solo querés ejecutar un comando, la cuestión es simple:

[[ -f "$archivo" ]] && echo "Existe."


¿Te vas dando cuenta de cómo funciona esto?

Ahora... ¿qué pasa si querés ejecutar varios comandos en una sola línea?
Para esto mismo es que están las llaves "{}", las cuales se pueden utilizar en combinación con "&&" (AND - "si esto es cierto, hacé lo siguiente") y "||" (OR - "si lo anterior falló, hacé esto").

[[ -f "$archivo" ]] && { echo "Es un archivo regular."; exit; }
[[ -d "$archivo" ]] && { echo "Es un directorio."; exit; }
echo "No se encontró nada con ese nombre."


Este código es equivalente al "if" completo (if/else/elif/fi) de más arriba
Son 3 líneas en comparación con 7: menos de la mitad,

Y podemos llevarlo al extremo "oneliner", que es una técnica que yo usaba mucho cuando programaba en BASIC, hace muchos años atrás:

[[ -f "$archivo" ]] && { echo "Es un archivo regular."; exit; };[[ -d "$archivo" ]] && { echo "Es un directorio."; exit; };echo "No se encontró nada con ese nombre."

Una sola línea. ¿Qué me contás?

Reglas de oro para usar las llaves.

  • Espacios obligatorios: siempre debe haber un espacio después de { y antes de }
  • El punto y coma final: el último comando dentro de las llaves debe terminar en punto y coma antes de cerrar la llave, para que BASH sepa que el bloque terminó. Esto no es como lo grotesco de Java, al que hay que estar avisándole en cada línea que la línea se terminó, poniendo SIEMPRE ";". Esto en BASH significa "se terminó la línea", pero NO ES OBLIGATORIO, y de hecho, se usa extremadamente poco.


¿Cuándo utilizar cada estilo?

Debes utilizar el "if/elif/else/fi" tradicional cuando se trate de una lógica compleja o estés escribiendo un script que otros (o vos mismo en el futuro) necesiten leer con total claridad y sin andar interpretando "código barroco".

Te recomiendo que, si vas a utilizar este método de "&&", "||" y llaves "{ ...; }", lo hagas ante una validación rápida y corta. Ahorra mucho espacio y, una vez que te acostumbrás a leerlo, hace que tu código se vea mucho más dinámico y fluido.

Flag Counter Visitas previas a la existencia de este contador: 3433

Artículos aleatorios

    Páginas: