Cadenas de texto en Bash

Manejar cadenas con caracteres no ASCII y especiales en Bash

Manejar cadenas con caracteres no ASCII y caracteres especiales en Bash puede ser realmente un dolor de cabeza debido a cómo Bash interpreta y manipula las cadenas. Los problemas suelen provenir de configuraciones de localización, inconsistencias en el uso de comillas y la forma en que Bash trata ciertos caracteres (como espacios, saltos de línea o caracteres de expansión). Afortunadamente, hay estrategias prácticas para mitigar estos problemas en la programación diaria con scripts. Vamos a desglosarlo y explorar soluciones, incluyendo funciones de escape y convenciones.

Problemas comunes

  • Caracteres no ASCII: Bash puede manejar mal UTF-8 u otras codificaciones si la localización no está configurada correctamente, lo que lleva a salidas distorsionadas o comportamientos inesperados.
  • Caracteres especiales: Caracteres como *, ?, $, comillas (' y ") y saltos de línea pueden desencadenar expansiones, sustituciones o simplemente romper comandos si no se manejan con cuidado.
  • Problemas de comillas: El uso incorrecto de comillas (o la falta de ellas) puede hacer que las cadenas se dividan inesperadamente o pierdan su significado previsto.

Estrategias para evitar estos problemas

1. Configurar una localización adecuada

Asegúrate de que tu entorno use una localización consistente que soporte UTF-8, que es estándar para manejar caracteres no ASCII. Agrega esto a tu script o .bashrc:

export LC_ALL=es_ES.UTF-8
export LANG=es_ES.UTF-8

Esto asegura que Bash y herramientas como grep, sed o awk interpreten correctamente los caracteres no ASCII.

2. Usar comillas robustas

Siempre usa comillas en variables y cadenas para evitar la división de palabras y la expansión de comodines. Por ejemplo:

cadena="hola * mundo"
echo "$cadena"  # Salida: hola * mundo
echo $cadena    # Salida: hola (y potencialmente lista archivos debido a *)

Usa comillas dobles ("$var") a menos que necesites específicamente un comportamiento sin comillas. Las comillas simples ('$var') evitan toda expansión, lo que puede ser útil para cadenas literales.

3. Funciones de escape/desescape

Crear funciones para manejar el escape y el desescape puede estandarizar cómo lidias con caracteres especiales. Aquí hay un enfoque simple:

  • Función de escape: Usa printf '%q' para escapar una cadena de manera segura para su uso en Bash.
escapar_cadena() {
    printf '%q' "$1"
}

Ejemplo:

cadena="hola * mundo & \"comilla\""
escapada=$(escapar_cadena "$cadena")
echo "$escapada"  # Salida: hola\ \*\ mundo\ \&\ \"comilla\"
  • Función de desescape: Revertir esto es más complicado ya que Bash no tiene un desescape incorporado, pero puedes usar eval con precaución:
desescapar_cadena() {
    eval "printf '%s' \"$1\""
}

Ejemplo:

escapada="hola\ \*\ mundo\ \&\ \"comilla\""
desescapada=$(desescapar_cadena "$escapada")
echo "$desescapada"  # Salida: hola * mundo & "comilla"

Precaución: eval puede ser peligroso con entradas no confiables—úsalo solo si controlas la fuente de la cadena escapada.

4. Usar arreglos en lugar de cadenas

Para cadenas complejas con espacios o caracteres especiales, los arreglos de Bash son una alternativa más segura a las cadenas simples:

arreglo=("hola * mundo" "otra & cadena")
echo "${arreglo[0]}"  # Salida: hola * mundo

Pasa arreglos a comandos así:

printf '%s\n' "${arreglo[@]}"

5. Adoptar una convención

Una convención consistente puede evitar muchos problemas. Aquí hay una práctica:

  • Convención: Siempre almacena cadenas en variables con comillas dobles o arreglos, y escapa caracteres especiales solo cuando se pasen a comandos externos o contextos de eval.
  • Reglas:
    1. Usa "$var" para todas las expansiones de variables.
    2. Usa arreglos ("${arreglo[@]}") cuando manejes listas o cadenas que puedan contener espacios/saltos de línea.
    3. Escapa cadenas generadas dinámicamente con printf '%q' antes de almacenarlas si se reutilizarán en un comando.
    4. Evita depender de la división de palabras o la expansión de comodines—contrólalo explícitamente cuando sea necesario.

Ejemplo:

texto="hola * mundo"
texto_seguro=$(escapar_cadena "$texto")
comando=(echo "$texto_seguro")
"${comando[@]}"  # Salida segura: hola\ \*\ mundo

6. Aprovechar herramientas externas

Bash no es excelente manipulando cadenas—herramientas como sed, awk o tr suelen ser más confiables:

  • Reemplazar caracteres especiales: echo "$cadena" | sed 's/[*&]/\\&/g'
  • Limpiar no ASCII: echo "$cadena" | tr -cd '[:print:]\n'

7. Probar con casos extremos

Siempre prueba tus scripts con cadenas que contengan espacios, saltos de línea, comillas y caracteres no ASCII (por ejemplo, café, π o emojis). Esto ayuda a detectar problemas temprano.

¿Cuál es la mejor estrategia?

  • A corto plazo: Usa comillas religiosamente ("$var") y printf '%q' para escapar cuando sea necesario. Es rápido y funciona en la mayoría de los casos.
  • A largo plazo: Cambia a arreglos para cosas complejas y confía en funciones de escape/desescape para cadenas dinámicas. Combina esto con una localización UTF-8.
  • Convención: Mantén “todo entre comillas, escapa solo al ejecutar” como predeterminado. Es simple y minimiza sorpresas.

Para la programación diaria en Bash, combinar comillas adecuadas, arreglos y un escape ocasional con printf '%q' manejará el 90% de las rarezas sin complicar demasiado las cosas. Si tus scripts crecen demasiado, considera un lenguaje como Python, que tiene un mejor manejo de cadenas incorporado. ¡Bash es poderoso, pero no es un maestro de las cadenas!

By Pablo Machón | Computer Science, IT, Libre Software | Written with a little help from my friends

Copyright 2025 Pablo Machón | published under the GFDL License | You may use, copy, modify and redistribute this page as long as you cite the author. Please read the license