53 votes

Noms de fichiers avec espaces rompant boucle for, commande find

J'ai un script qui recherche tous les fichiers dans plusieurs sous-dossiers et les archive en tar. Mon script est

for FILE in `find . -type f  -name '*.*'`
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

La commande find me donne le résultat suivant

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

Mais le DOSSIER la variable ne stocke que la première partie du chemin ./F1/F1-2013-03-19 puis la partie suivante 160413.csv .

J'ai essayé d'utiliser read à l'aide d'une boucle "while",

while read `find . -type f  -iname '*.*'`;   do ls $REPLY; done

mais j'obtiens l'erreur suivante

bash: read: `./F1/F1-2013-03-19': not a valid identifier

Quelqu'un peut-il suggérer une autre solution ?

Update

Comme suggéré dans les réponses ci-dessous, j'ai mis à jour les scripts.

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

Le résultat que j'obtiens est le suivant

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors

57voto

Michal Bernhard Points 2203

使用する for con find n'est pas la bonne approche, voir par exemple cet article de la boîte de Pandore que vous êtes en train d'ouvrir.

L'approche recommandée consiste à utiliser find , while y read comme décrit aquí . Vous trouverez ci-dessous un exemple qui devrait vous convenir :

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

De cette manière, vous délimitez les noms de fichiers par des caractères nuls ( \0 ), cela signifie que les variations d'espace et d'autres caractères spéciaux ne posent pas de problèmes.

Afin de mettre à jour une archive avec les fichiers que les find vous pouvez passer sa sortie directement à tar :

find . -type f -name '*.*' -printf '%p\0' | 
tar --null -uf archive.tar -T -

Notez que vous ne devez pas faire de différence entre l'existence ou non de l'archive, tar le gérera de manière raisonnable. Notez également l'utilisation de -printf ici pour éviter d'inclure le ./ dans les archives.

20voto

ShawnMilo Points 309

Cela fonctionne et c'est plus simple :

find . -name '<pattern>' | while read LINE; do echo "$LINE" ; done

Crédit à Rupa ( https://github.com/rupa/z ) pour cette réponse.

18voto

kiri Points 25860

Essayez de citer le for boucle comme celle-ci :

for FILE in "`find . -type f  -name '*.*'`"   # note the quotation marks

Sans guillemets, bash ne gère pas les espaces et les nouvelles lignes ( \n )...

Essayez également de définir

IFS=$'\n'

4voto

steeldriver Points 118154

En plus d'une citation correcte, vous pouvez dire find pour utiliser un séparateur NULL, puis lire et traiter les résultats dans un fichier while boucle

while read -rd $'\0' file; do
    something with "$file"
done < <(find  . -type f -name '*.*' -print0)

Cela devrait permettre de traiter tous les noms de fichiers conformes à POSIX - voir man find

   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.

2voto

Drake Clarris Points 1031

Je pense qu'il est préférable d'utiliser find de l'option -exec.

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

Find exécute alors la commande en utilisant un appel système, de sorte que les espaces et les nouvelles lignes sont préservés (plutôt qu'un pipe, qui nécessiterait la citation de caractères spéciaux). Notez que "tar -c" fonctionne que l'archive existe déjà ou non, et que (au moins avec bash) ni {} ni + n'ont besoin d'être cités.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X