web-dev-qa-db-fra.com

Comment puis-je lire une liste de noms de fichiers à partir d'un fichier dans bash?

J'essaie d'écrire un script bash qui traitera une liste de fichiers dont les noms sont stockés un par ligne dans un fichier d'entrée, quelque chose comme

find . -type f -mtime +15 > /tmp/filelist.txt
for F in $(cat /tmp/filelist.txt) ; do
  ...
done;

Mon problème est que les noms de fichiers dans filelist.txt peuvent contenir des espaces, donc l'extrait ci-dessus étendra la ligne

my text file.txt

à trois noms de fichiers différents, my, text et file.txt. Comment puis-je résoudre ce problème?

31
agnul

Utilisez read:

while read F  ; do
        echo $F
done </tmp/filelist.txt

Vous pouvez également utiliser IFS pour modifier la façon dont le shell sépare votre liste:

OLDIFS=$IFS
IFS="
"
for F in $(cat /tmp/filelist.txt) ; do
  echo $F
done
IFS=$OLDIFS

Alternativement (comme suggéré par @tangens), convertissez le corps de votre boucle en un script séparé, puis utilisez l'option -exec de find pour l'exécuter si pour chaque fichier trouvé directement.

41
Douglas Leeder

Vous pouvez le faire sans fichier temporaire en utilisant la substitution de processus:

while read F
do
  ...
done < <(find . -type f -mtime +15)
7

utiliser pendant la lecture

echo $FILE | while read line
do
echo $line
done

Vous pouvez faire une redirection au lieu d'un écho

3
DVK

Vous pouvez utiliser le -exec paramètre de find et utilisez directement les noms de fichiers:

find . -type f -mtime +15 -exec <your command here> {} \;

Le {} est un espace réservé pour le nom de fichier.

2
tangens

redirigez votre commande find directement vers la boucle de lecture

find . -type f -mtime +15 | while read -r line
do
   printf "do something with $line\n"
done
1
ghostdog74

Je ne suis en aucun cas un expert bash (j'écris habituellement mon script en Ruby ou python pour être multiplateforme), mais j'utiliserais un expression rationnelle pour échapper aux espaces de chaque ligne avant de la traiter.

Pour Bash Regex: http://www.linuxjournal.com/node/1006996

Dans une situation similaire en Ruby (traitement d'un fichier csv et nettoyage de chaque ligne avant de l'utiliser)):

File.foreach(csv_file_name) do |line| 
    clean_line = line.gsub(/( )/, '\ ') 
    #this finds the space in your file name and escapes it    
    #do more stuff here
end  
0
konung