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 rango
“a-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! | BorradorLas transformaciones sucesivas son:
Después de sed 's/|.*//':
¿Cómo funciona BASH? ¡Explicación completa!
Después de sed 's/[^a-zA-Z0-9 ]//g':
Cmo funciona BASH Explicacin completa
Después de sed 's/ /-/g':
Cmo-funciona-BASH-Explicacin-completa-
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