Nombres de ficheros con espacios, la variable IFS

PROBLEMA:

Al hacer bucles for dentro de un script sobre los nombres de ficheros de un directorio, si los nombres de los ficheros no tienen espacios en blanco el bucle itera correctamente, pero si los nombres de los ficheros contienen espacios en blanco entonces el bucle parte los nombres de los ficheros. Un ejemplo del problema es el siguiente:

[francisco@Terminator prueba]$ ls
fichero_1.txt  fichero_2.txt  fichero 3.txt  fichero 4.txt
[francisco@Terminator prueba]$ for i in $(ls *); do echo $i; done
fichero_1.txt
fichero_2.txt
fichero
3.txt
fichero
4.txt

En el ejemplo el bucle simplemente lista los ficheros, pero se puede ver como lo hace mal. En scripts complejos esto es una fuente de errores constante, ya que aunque en Linux se desaconseja tener nombres de ficheros que contengan espacios, en la práctica es algo prácticamente inevitable.

SOLUCIÓN:

Usar la variable especial IFS, esta variable contiene los caracteres que son usados para partir las lineas en palabras que llegan desde la entrada estandar. El valor por defecto de esta variable es espacio, <tabulador><nueva línea>. Se puede imprimir su valor usando el comando:

cat -etv <<<"$IFS"

La solución pasa por sobreescribir el valor de IFS, y darle el valor <tabulador><nueva linea>. Esto se puede hacer de la siguiente forma:

IFS=$'\x0A'$'\x0D'

\x0D no es mas que 13 en hexadecimal y \x0A representa 10 en hexadecimal. En la tabla ASCII se corresponden con los caracteres CR y LF. O también si se prefiere, se puede hacer de forma menos matemática:

IFS=$(echo -en "\n\b");

Con las modificaciones pertinentes sobre la variable IFS, el script quedaría de la siguiente manera:

francisco@Terminator prueba]$ IFS=$(echo -en "\n\b"); for i in $(ls *); do echo $i; done
fichero_1.txt
fichero_2.txt
fichero 3.txt
fichero 4.txt

Si va a usar esto dentro de un script y luego pretende trabajar en la misma shell, tal vez le interese salvar el valor de IFS y volverlo a poner en la propia variable una vez termine el bucle. Tal y como se ilustra a continuación:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for f in *
do
  echo "$f"
done
IFS=$SAVEIFS

Como se puede observar el resultado no tiene nada que ver con el resultado que ofrecía el bucle al principio.

Fuentes:

Acerca de franciscoguemes

Ingeniero en Informática
Esta entrada fue publicada en Bash. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s