[Shell] boucle avec des nom de fichier avec espace

boucle avec des nom de fichier avec espace [Shell] - Shell/Batch - Programmation

Marsh Posté le 03-06-2008 à 18:52:26    

Salut,
Je souhaiterai faire :

Code :
  1. for i in `ls`
  2. (traitement sur chaque fichier)


Le problème est que si le répertoire contient des fichiers, ayant des espaces dans leur nom, sa ne marche pas  :(

Reply

Marsh Posté le 03-06-2008 à 18:52:26   

Reply

Marsh Posté le 03-06-2008 à 19:51:06    

Sauf que le probleme n'est pas sur la ligne 1, mais sur le reste, que tu ne nous a pas détaillé.
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 04-06-2008 à 09:24:29    

Si, il y a déjà un problème sur la ligne 1 : il faut qu'il mette des double quotes autour du `ls`, sinon i prendra successivement le nom de chacun des composants de chaque nom de fichier contenant des espaces.
 
Ensuite suivant ce qu'il fait dans la boucle il faudra éventuellement également qu'il mettre des double quotes autour de $i.
 

for i in "`ls`"
do
        abc "$i" def
done

Reply

Marsh Posté le 04-06-2008 à 15:13:46    

matafan a écrit :

Si, il y a déjà un problème sur la ligne 1 : il faut qu'il mette des double quotes autour du `ls`, sinon i prendra successivement le nom de chacun des composants de chaque nom de fichier contenant des espaces.
 
Ensuite suivant ce qu'il fait dans la boucle il faudra éventuellement également qu'il mettre des double quotes autour de $i.
 

for i in "`ls`"
do
        abc "$i" def
done



 
Le fait de mettre des guillemets autour du ls ne changera rien. Ou dans le meilleur des cas il prendra l'ensemble de tous les noms comme un seul argument. Mais mettre des guillemets à "$i" est une excellente idée.
 
Puisque le problème vient du for (qui utilise l'espace pour séparer ses éléments), ben faut s'en affranchir. Le read est tout indiqué pour ça puisque lui se cale sur le <return> et que toute ligne (même venant d'une commande) se termine par un <return>...

Code :
  1. #!/bin/bash
  2. ls |while read i
  3. do
  4.   abc "$i" def
  5. done


Petit inconvénient => qui dit pipe dit sous processus. Et donc on ne pourra faire ressortir aucune variable de la boucle. Si ce point ne présente pas de soucis alors c'est bon. Sinon on peut gruger un petit peu en utilisant des parenthèses

Code :
  1. #!/bin/bash
  2. ls |( while read i
  3. do
  4.   abc "$i" def
  5.   var=machin
  6. done
  7. echo $var )


Mais cela ne fait que reporter le problème. Si vraiment on veut s'en affranchir totalement, on ne peut plus utiliser le pipe donc faut passer par un fichier temporaire

Code :
  1. #!/bin/bash
  2. # Création du fichier temporaire
  3. ls >fichier_temporaire
  4.  
  5. # Création du buffer input contenant le fichier
  6. exec 3<fichier_temporaire
  7.  
  8. # Le fichier ne sert plus à rien car il est dans le buffer
  9. rm -f fichier_temporaire
  10.  
  11. # Lecture du buffer
  12. while read i 0<&3
  13. do
  14.   abc "$i" def
  15.   var=machin
  16. done
  17. echo $var


Cette solution évite les problèmes mentionnés précédement mais on entre dans une autre problématique concernant les accès multiples sur le même fichier (si le script est lancé deux fois en parallèle => dommage pour "fichier_temporaire" ) qui ne peut se régler que par la création d'un fichier temporaire au nom unique (en incluant par exemple dans son nom la variable "$$" => n° process)
 
Comme quoi, en partant d'un problème tout con, si on veut tout gérer on peut arriver à des sacrés soucis...

Message cité 1 fois
Message édité par Sve@r le 04-06-2008 à 15:19:20

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 04-06-2008 à 15:35:11    

Sve@r a écrit :

Le fait de mettre des guillemets autour du ls ne changera rien. Ou dans le meilleur des cas il prendra l'ensemble de tous les noms comme un seul argument.


C'est faux. Tu te t'en serait rendu compte si comme moi tu avais essayé ;)
Pour info l'explication est dans le man de bash :

Citation :

  Command Substitution
       [...]
       If the substitution appears within double quotes,  word  splitting  and
       pathname expansion are not performed on the results.


S'il fait comme j'ai indiqué, ça marchera.

Message cité 1 fois
Message édité par matafan le 04-06-2008 à 15:37:23
Reply

Marsh Posté le 04-06-2008 à 15:37:52    

Citation :

Le fait de mettre des guillemets autour du ls ne changera rien. Ou dans le meilleur des cas il prendra l'ensemble de tous les noms comme un seul argument. Mais mettre des guillemets à "$i" est une excellente idée.

Avec un `ls -Q` on ne recupere pas le nom entre double quotes?. Bon, il y a peut être des options a mettre en plus pour n'avoir que le nom du fichier.
A+,
 


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 04-06-2008 à 15:41:08    

matafan a écrit :


C'est faux. Tu te t'en serait rendu compte si comme moi tu avais essayé ;)
Pour info l'explication est dans le man de bash :

Citation :

  Command Substitution
       [...]
       If the substitution appears within double quotes,  word  splitting  and
       pathname expansion are not performed on the results.


S'il fait comme j'ai indiqué, ça marchera.

L'explication en question dit qu'il doit prendre tous les noms comme un seul argument, non?
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 04-06-2008 à 18:36:10    

Excusé moi de ne pas avoir été assez clair. Je voulais récupérer chaque nom de fichier .
Merci beaucoup Sve@r c'est ce que je cherchais !

Reply

Marsh Posté le 04-06-2008 à 21:47:36    

Bon j'ai fumé pour le "`ls`", effectivement ça renvoit tout comme un mot.
 
La solution la plus simple c'est quand même "for i in *" ...

Reply

Marsh Posté le 05-06-2008 à 21:31:12    

matafan a écrit :

Bon j'ai fumé pour le "`ls`", effectivement ça renvoit tout comme un mot.


Non t'as pas fumé. Auourd'hui j'ai essayé cette syntaxe sur un bash de Fedora Core 7 et effectivement ça marche.
 
Cependant, travaillant dans un milieu hétérogène (avec du sun, du Redhat, du Ubuntu) et étonné de ce résultat qui ne correspondait pas à ce que j'attendais, j'ai testé sur un bash de Solaris puis un ksh => dans les deux cas cela redonne ce que j'avais prévu => un seul mot.
Donc il est probable que cette syntaxe devienne un standard dans le futur mais cela ne l'est pas actuellement et celui qui veut faire un script "portable descendant" ne peut pas l'utiliser.
 
Sinon l'option "ls -Q" de Gilou fonctionne aussi sur FC7 mais pas sur Solaris. Là aussi pas standard.


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 05-06-2008 à 21:31:12   

Reply

Marsh Posté le 06-06-2008 à 04:00:20    

Oui, ls -Q c'est pas Posix, donc pas OSXien, par contre ca a l'air d'être Gnuesque et Linuxien.
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 23-11-2009 à 14:56:39    

Je déterre le sujet pour ceux qui passent et ont le même soucis (on le rencontre assez souvent quand même) une solution assez simple existe:
 

Code :
  1. OLDIFS=$IFS;
  2. IFS=$(echo -en "\n\b" );
  3. for i in `ls`;
  4. do
  5. ...
  6. done;
  7. IFS=$OLDIFS;


 
NB: Si on utilise rien d'autre qui a besoin d'IFS sauvegarder sa valeur n'est même pas obligatoire car une fois le script fini il reprend sa valeur normale.

Reply

Marsh Posté le 23-03-2012 à 16:37:25    

Merci de l'avoir sorti des oubliettes HDDSYonex.
Ca marche nikel :)

Reply

Marsh Posté le 23-03-2012 à 18:42:16    

Tu peux aussi bêtement protéger, t'es pas obligé de changer IFS c'est un peu bourrin :o

for i in "$(ls)"; do  
    echo "$i"
done



---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 25-03-2012 à 16:48:31    

salut,
 
et si tout simplement vous utilisiez les possibilités du shell : le développement des chemins

Code :
  1. for f in *; do echo "$f"; done

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed