Comprendiendo “sed” en “GeneWeb”: una mirada crítica al código barroco generado por IA.

Comprendiendo “sed” en “GeneWeb”: una mirada crítica al código barroco generado por IA.


sed: una herramienta tan contraintuitiva como poderosa.

Introducción: el problema del código generado por IA.

Cuando trabajamos con inteligencias artificiales para generar código, nos encontramos con un dilema fundamental: las IAs resuelven problemas de manera eficiente, pero respondiendo a su propio límite de “tokens por chat” y con “cabeza máquina”.
Por estas mismas razones es que normalmente producen código compacto y críptico que sacrifica la legibilidad. Este es precisamente el caso que vamos a analizar hoy.

En “GeneWeb” (un generador de páginas HTML estáticas escrito en BASH), necesitaba transformar títulos de artículos en nombres de archivo válidos. La IA me sugirió esta línea:

nombre_base=$(echo "$titulo" | sed 's/|.*//; s/[^a-zA-Z0-9 ]//g; s/ /-/g' | tr '[:upper:]' '[:lower:]')

Funciona perfectamente, pero… ¿qué “diantres” hace exactamente? ¿Podés leerla y entenderla de un vistazo? Muy probablemente no, a menos que seas un experto en “sed”. En lo personal, estuve rato tratando de entender qué hacía “sed” ahí. Y aquí radica el problema: en el “código que funciona en base a tu pérdida de autonomía como programador.

Por eso -y no creas que me resultó fácil- decidí reformularla de esta manera:

nombre_base=$(echo "$titulo" |\     # "|\" se usa como "continuador" (al otro renglón).
    sed 's/|.*//' |\                # Quitar todo después de "|". 
    sed 's/[^a-zA-Z0-9 ]//g' |\     # Quitar caracteres especiales. 
    sed 's/ /-/g' |\                # Convertir espacios a guiones. 
    tr '[:upper:]' '[:lower:]')     # Convertir a minúsculas.

Esto hace exactamente lo mismo, pero ahora puedo leerlo, entenderlo, modificarlo… y EXPLICARLO.


Pero, esperá…
Ahora mismo, en el momento de la redacción de esta entrada, me doy cuenta de que a esto

tr '[:upper:]' '[:lower:]')     # Convertir a minúsculas.

yo no lo hubiera hecho así, sino que hubiera utilizado algo más sencillo y fácil de recordar, estilo "${nombre_base,,}"...

Entonces... Caray, necesito volver a reformular…

nombre_base=$(echo "$titulo" |\     # "|\" se usa como "continuador en el otro renglón".
    sed 's/|.*//' |\                # Quitar todo después de "|". 
    sed 's/[^a-zA-Z0-9 ]//g' |\     # Quitar caracteres especiales. 
    sed 's/ /-/g')                  # Convertir espacios a guiones.

nombre_base="${nombre_base,,}"      # Convertir a minúsculas.

Ahora sí es mi estilo, mi manera de hacer las cosas. Ahora sí voy a proceder a desglosar cada parte con enorme gusto. Antes, ni siquiera me “olía bien” ese código primario, barroco, feo, críptico, entreverado y contraintuitivo, como el escritorio Gnome, por ejemplo. ¿O no?



 
La estructura general: sustitución de comandos y tuberías (o “pipelines”).

Antes de “entrarle de lleno” a “sed”, veamos la estructura general del código resultante y una explicación por adelantado.

Las tuberías (o “pipelines”) son “el resultado de una fase que se convierte en la próxima… Algo así como la “carrera de postas”, en donde, si el corredor “anterior” no resolvió su parte, tampoco podrán hacerlo los subsiguientes.

nombre_base=$(...)

Esto es conocido en programación como “sustitución de comandos”. Todo lo que esté dentro de $(...) se ejecuta, y su salida no se envía a la terminal, sino que se le asigna a una variable; en este caso: “nombre_base". En esta idea me basé para generar el “doble búfer” en mis juegos ASCII en BASH: en lugar de “imprimir directamente en pantalla”, mando todo un escenario gráfico para adentro de una variable y llegado el momento muestro el contenido de esa variable en pantalla (en lugar de ir mostrando paso a paso la construcción de dicho escenario).

Si soy o fui tu profe, sabrás que me voy por las ramas a veces… Me acaba de pasar… Recapitulando…

Dentro, tenemos una tubería. Te lo ejemplifico con “echo” (y simplificado) para que quede más claro.

echo "$titulo" | comando_1 | comando_2 | comando_3 | comando_4

Cada “|”, toma la salida del comando anterior y se la pasa como entrada al siguiente. Al ejemplo del corredor de postas, podemos sumarle el siguiente: una cadena de montaje donde cada estación procesa el producto y lo pasa a la siguiente. A propósito… ¿viste “Tiempos modernos” de Charles Chaplin? Deberías...

El “backslash” (\) al final de cada línea, le dice a BASH (en dialecto rioplatense “uruguayizado”): “esto no termina acá; no te pongás a descansar que hay que seguir laburando en la siguiente línea, valor”. Es código netamente estético, creado para mejorar la legibilidad y forzar a que algo “estrambótico” y que “suene” a “pegado con cinta” dentro de BASH, quede más agradable “al ojo”. Pero, aunque la mona se vista de seda...


 
Primera transformación:
sed 's/|.*//'

Esta es la primera llamada a “sed”. Me da “sed” de solo mirarla. Vamos a diseccionarla completamente.

Pero… Primero que nada, y ahora sí...

¿Qué es “sed”?

"sed" (Stream EDitor) es un editor que procesa texto caracter por caracter, línea por línea, secuencialmente, aplicando transformaciones según las instrucciones que le des. Su sintaxis es notoriamente críptica, herencia de su origen en los años 70. A propósito… ¿te suena “UNIX”?
Si Hugo es tu profesor y aún no te habló de eso, levantá la mano ya mismo, decí “¡sacrilegio!” y decile a Hugo que “el Hugo digital” te dijo que “el hugo real” tiene que explicar ya mismo qué es UNIX. Y como “penitencia”, decile que también tiene que explicar qué es KISS y qué es POSIX.

Anatomía del comando.

La estructura general de una sustitución en sed es:

sed 's/patrón/reemplazo/banderas'
  • "s", significa “sustituír”

  • el primer “/”, es el delimitante inicial para el proceso “patrón a buscar”,

  • el segundo “/”, es el delimitante final para el proceso “patrón a buscar”, y a su vez, también el “delimitante inicial para el proceso reemplazo”.

  • el tercer “/”, o bien cierra el comando, o bien es el delimitante inicial para la colocación de banderas, aunque las banderas son opcionales y en este ejemplo no las usaremos, para no complejizar más al asunto. ¿Viste cuánta seda precisa la mona?

En nuestro caso:

sed 's/|.*//' 
  • patrón a buscar: "|.*"

  • reemplazo: (vacío; nada entre "//")

  • banderas: ninguna.

Descifrando al patrón: "|.*"

Acá es en donde la cosa se empieza a poner... interesante... “ponele”…
Este patrón usa expresiones regulares:

  • "|" es el caracter literal “pipe” (barra vertical),

  • "." significa “cualquier caracter”,

  • "*" significa “tantas veces como sea necesario”.

Entonces, “|.*” significa: “encontrá el primer pipe | y todo lo que venga después de él, sin importar qué $%&/# sea”.
¿Notás que “pipe” es nuestro delimitador representado por “|”, y que ese delimitador podría ser cualquier otro, o bien podrían ser varios? En GeneWeb -y también en otros de mis programas, como en “BurocraTux”-, utilizo a “sed” con el delimitador “///” (en lugar de “|”) y el resultado es el mismo.

Ejemplo práctico.

Si mi título es “Mi primer artículo | Borrador”:

el patrón “|.*” encuentra “| Borrador” (el “pipe” -o delimitador- y todo lo demás), y lo reemplaza por “nada” (es decir, lo elimina). Resultado: “Mi primer artículo”.

Importante: “sed”, por omisión, solamente busca la primera coincidencia en cada línea.
Y aquí es en donde entran en juego las banderas: si quisiéramos eliminar (o sustituir) todas las ocurrencias y no solo la primera, deberíamos usar la bandera “g” (global).


 
Segunda transformación: sed 's/[^a-zA-Z0-9 ]//g'

sed 's/[^a-zA-Z0-9 ]//g'

Esta es la más compleja de todas. Vamos por partes.

La “clase de caracteres”: [...]

Los corchetes [] definen una clase de caracteres, es decir, un conjunto de caracteres válidos. Por ejemplo:

  • [abc] coincide con a, b o c

  • [0-9] coincide con cualquier dígito

  • [a-z] coincide con cualquier letra minúscula

La negación: [^...]

El acento circunflejo ^ al inicio de una clase de caracteres, niega esa clase:

  • [^abc] coincide con cualquier caracter excepto a, b o c

  • [^0-9] coincide con cualquier caracter que no sea un número.

Desglosando [^a-zA-Z0-9 ]

Esta clase dice: “cualquier caracter que NO sea”:

  • a-z: letras minúsculas de la a la z,

  • A-Z: letras mayúscula de la A la Z,

  • 0-9: dígito (números del 0 al 9),

  • : espacio (ese espacio “que no está negado” -después del 9- es importante).

En otras palabras: elimina todo excepto letras, números y espacios.

La bandera global “g"

sed 's/[^a-zA-Z0-9 ]//g'

La “g" al final, significa “global” y aplica el reemplazo a todas las coincidencias, no solamente a la primera, tal como lo comenté antes.

Ejemplo práctico.

Si tenemos “¿Cómo funciona BASH? ¡Explicación completa!”, los caracteres que no son letras, números o espacios, son, por ejemplo: ¿, ó, ?, ¡, !

Después de correr la línea de más arriba aplicándola a esta frase, el resultado sería el siguiente:

"Cmo funciona BASH Explicacin completa".

Nota: observá que la “ó" desapareció completamente porque no está en el rangoa-zA-Z", el cual no contempla vocales con acento. Esto es una limitación intencional para generar nombres de archivo seguros.


 
Tercera transformación: sed 's/ /-/g'

sed 's/ /-/g' 

Así como la anterior era la más difícil, acá te traigo el “cafecito con Massini”… ¿No conocés el postre “Massini”? Pues… Deberías mirar “Tiempos modernos” desde un sistema “UNIX-like”, y comiendo un Massini.

Esta es la más sencilla de todas:

  • patrón: “ “ (un espacio),

  • reemplazo: “-” (un guión),

  • bandera: “g” (global, todos los espacios y no solo el primero que “haiga”).

Simplemente convierte todos los espacios en guiones.

Ejemplo práctico

Siguiendo con nuestro ejemplo, la frase “Cmo funciona BASH Explicacin completa”, se convertirá en: “Cmo-funciona-BASH-Explicacin-completa”.


 
Cuarta transformación: tr '[:upper:]' '[:lower:]'

tr '[:upper:]' '[:lower:]'

Finalmente, salimos de sed y usamos tr (translate, traducir).

¿Qué es “tr”, aparte del sonido de un redoblante?

¡No te asustes, que “tr” es más simple que “sed” y además ya estamos terminando!
“tr
traduce o elimina caracteres. Su sintaxis básica es:

tr 'conjunto_1' 'conjunto_2'

Reemplaza cada caracter del “conjunto_1” con el caracter correspondiente del “conjunto_2”.

Las clases POSIX: [:upper:] y [:lower:]

¿Te acordás de que antes hablamos de lo que era una clase? ¿Te diste cuenta de que las podías crear vos? Pues, bien: también hay clases predefinidas, por ejemplo estas 2:

  • [:upper:] representa a todas las letras mayúsculas,

  • [:lower:] representa a todas las letras minúsculas.

Esto es más robusto que usar A-Z y a-z porque funciona correctamente con diferentes “locales” (variables del sistema para definir el área de localización geográfica, los formatos de fecha, hora, moneda, etc).

Ejemplo práctico.

El texto "Cmo-funciona-BASH-Explicacin-completa", al ser "pasado por el tamiz" de estas clases, se
convertiría en "cmo-funciona-bash-explicacin-completa".



El resultado final

Partiendo del título original:

¿Cómo funciona BASH? ¡Explicación completa! | Borrador

Las transformaciones sucesivas son:

  1. Después de sed 's/|.*//':

    ¿Cómo funciona BASH? ¡Explicación completa! 
  2. Después de sed 's/[^a-zA-Z0-9 ]//g':

    Cmo funciona BASH Explicacin completa 
  3. Después de sed 's/ /-/g':

    Cmo-funciona-BASH-Explicacin-completa-
  4. Después de tr '[:upper:]' '[:lower:]':

    cmo-funciona-bash-explicacin-completa-

Y ese sería el contenido de la variable nombre_base, que luego se usaría para generar el nombre del archivo HTML.

 



Comparando las dos versiones.

Código rígido generado por IA:

nombre_base=$(echo "$titulo" | sed 's/|.*//; s/[^a-zA-Z0-9 ]//g; s/ /-/g' | tr '[:upper:]' '[:lower:]')

Ventajas:

- código “oneliner” (ocupa una sola línea),
- es más “eficiente” (menor cantidad de código -y por eso la IA la elige-).

Desventajas:

- prácticamente ilegible para quien no domine sed
- los tres comandos sed están concatenados con “;” dentro de la misma invocación (barroquismo),
- difícil de modificar o “debuggear”
- perdés autonomía como programador: no entender qué hace cada parte, es no poder mejorarla: es como autoimponerte una “caja negra” cuando estás trabajando con open source… un contrasentido total.

Código “esponjoso” creado por un profesor que además de querer que tu programa funcione, quiere que entiendas cada “mísero” caracter que hayas puesto en tu programa:

nombre_base=$(echo "$titulo" |\ 
    sed 's/|.*//' |\ 
    sed 's/[^a-zA-Z0-9 ]//g' |\ 
    sed 's/ /-/g' |\ 
    tr '[:upper:]' '[:lower:]') 

Ventajas:

- legible: cada transformación es clara,
- comentable: podés agregar comentarios línea por línea (que fue lo que hice yo mismo),
- modificable: si necesitas cambiar una transformación, sabes exactamente dónde hacerlo,
- educativo: otros programadores pueden entenderlo, independientemente de su nivel de “nerdcidad”.

Desventajas:

- ocupa más líneas, ergo, más recursos de espacio,
- ¿hay realmente alguna otra desventaja?


 
Conclusión: código legible vs. código obsesivamente eficiente.

Este ejemplo ilustra un principio fundamental en programación: el código se escribe una vez, pero se lee muchas veces.

La versión “eficiente” de la IA ahorra código, pero a costa de la comprensión. En un script personal que compartís públicamente (como los míos en CodeBerg), la legibilidad es mucho más valiosa que la micro-optimización. En la vida de un profesor, también. Rara vez encontrarás a un profesor que enseñe de manera críptica. Se puede, y es un gran desafío, pero no es lo común. Lo que sí es común, es que profesores escriban de manera barroca y te digan “esto se hace así, anotátelo en el cuaderno porque es imposible de memorizar” y que siga con su clase: me ha pasado y soy enemigo acérrimo de ese tipo de “docente”.

El verdadero problema del código barroco generado por IA (y más con instrucciones que son un imán para las dificultades, como “sed” o “awk”) no es que esté mal: es que te puede alejar del control de tu propio código. Si no entendés cada parte de lo que hace tu programa, no sos dueño del mismo: sos su rehén. O sos rehén de la IA...

Por eso, aunque “sed” sea una herramienta poderosísima, su sintaxis barroca y contraintuitiva exige que siempre expliquemos lo que hacemos. Ya sea mediante comentarios, mediante código más “verborrágico”, o (como en este artículo) mediante documentación externa.

El conocimiento profundo de nuestras herramientas es lo que nos diferencia de quien simplemente copia y pega código que no comprende.

 



Referencias y recursos adicionales.

Si ahora te gustó el “temita este” y querés profundizar, te recomiendo:

  • GNU sed manual: "info sed" o "man sed"

  • Regular-Expressions.info: excelente recurso para entender expresiones regulares

  • El libro sed & awk de O’Reilly: el clásico definitivo sobre estas herramientas (podés encontrarlo en la biblioteca de Entropía binaria, en el grupo de Telegram, junto a otras joyas de este estilo).

Y recordá siempre: no hay preguntas tontas cuando se trata de entender tu propio código. Si algo te parece críptico, probablemente lo sea también para otros. Explicarlo es un acto de generosidad hacia la comunidad y hacia la fuerza digital ética y moral de tu yo del futuro.


0 Comentarios:

Publicar un comentario

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

Artículos aleatorios

    Páginas: