mar 01 2011

Gestion des espaces dans les boucles « for » avec IFS

Tag: ScriptsUggla @ 14 h 17 min

Une astuce pour gérer les espaces dans les boucles « for » en shell (bash, ksh).
Rien ne vaut un petit exemple pour montrer le problème :

root@hx04970: /root/home/root/nene # ll
total 0
-rw-r--r--   1 root       sys              0 Feb 28 14:09 1
-rw-r--r--   1 root       sys              0 Feb 28 14:09 2
-rw-r--r--   1 root       sys              0 Feb 28 14:09 3
drwxr-xr-x   2 root       sys             96 Feb 28 12:04 tata tutu
drwxr-xr-x   2 root       sys             96 Feb 28 12:03 titi
drwxr-xr-x   2 root       sys             96 Feb 28 12:03 toto

Imaginons que je fasse une boucle sur l’ensemble des fichiers de l’arborescence ci dessus.

root@hx04970: /root/home/root/nene # for i in *;do echo $i;done
1
2
3
tata tutu
titi
toto

Ici tout se passe bien, pas de problèmes, le répertoire « tata tutu » est bien interprété.

Maintenant je complique un peu en voulant faire une boucle sur les répertoires seulement :

root@hx04970: /root/home/root/nene # for i in $(find . -type d);do echo $i;done
.
./toto
./titi
./tata
tutu

Et la c’est le drame :) .

L’espace sur le répertoire « tata tutu » est vu comme un séparateur et le shell croit que nous avons 2 répertoires tata et tutu.
Le bon administrateur Unix sait qu’il faut éviter les espaces c’est mal. (C’est comme : on ne croise jamais les effluves : c’est mal –> cf Ghostbuster).

Cool ! Mais si le répertoire est un share cifs avec des utilisateurs de windows, il y a de grande chance que l’on retrouve plein d’espaces dans les noms de fichier.

La meilleure méthode de résolution du problème ci dessus est d’utiliser la « variable » IFS.
Un petit man ksh me donne :

IFS Internal field separators, normally space, tab,
and newline that are used to separate command
words resulting from command or parameter
substitution, and for separating words with the
special command read. The first character of the
IFS parameter is used to separate arguments for
the « $* » substitution (see Quoting below).

IFS est la « variable » qui définie le séparateur, par défaut espace, tab et nouvelle ligne.

En redéfinissant le séparateur, sans l’espace et le tab on obtient :

root@hx04970: /root/home/root/nene # IFS=$'\n' && for i in $(find . -type d);do echo $i;done
.
./toto
./titi
./tata tutu

Le problème est résolu.

Pour aller plus loin : http://tldp.org/LDP/abs/html/internalvariables.html#IFSH