web-dev-qa-db-fra.com

Les noms de fichiers avec des espaces brisés pour la boucle, commande find

J'ai un script qui cherche tous les fichiers dans plusieurs sous-dossiers et archives à 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 la sortie suivante

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 la variable FILE 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 avec 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 un autre moyen?

Mise à jour

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

La sortie que je reçois est

./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
31
Ubuntuser

Utiliser foravec findest une mauvaise approche ici, voir par exemple cette description à propos de la boîte de Pandore que vous ouvrez.

L'approche recommandée consiste à utiliser findname__, whileet readcomme décrit ici . Voici un exemple qui devrait fonctionner pour vous:

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 avec des caractères nuls (\0). Cela signifie que la variation d'espace et d'autres caractères spéciaux ne poseront pas de problèmes.

Afin de mettre à jour une archive avec les fichiers que findlocalise, vous pouvez passer sa sortie directement à tarname__:

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

Notez que vous ne devez pas différencier si l'archive existe ou non, tarla gérera de manière judicieuse. Notez également l'utilisation de -printf ici pour éviter d'inclure le bit ./ dans l'archive.

35
Thor

Essayez de citer la boucle for comme ceci:

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

Sans guillemets, bash ne gère pas bien les espaces et les retours à la ligne (\n) ...

Essayez aussi de mettre

IFS=$'\n'
11
kiri

Cela fonctionne et est plus simple:

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

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

8
ShawnMilo

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

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

Cela devrait gérer tous les noms de fichiers compatibles 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.
4
steeldriver

J'ai fait quelque chose comme ça pour trouver des fichiers pouvant contenir des espaces.

IFS=$'\n'
for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do
    file $FILE | tee -a $LOG
done

Travaillé comme un charme :)

1
Scott B
find . <find arguments> -print0 | xargs -0 grep <pattern>
1
user2802945

La plupart des réponses sont interrompues s’il existe un caractère de nouvelle ligne dans le nom du fichier. J'utilise la bash depuis plus de 15 ans, mais uniquement interactive.

Dans Python, vous pouvez nous os.walk (): http://docs.python.org/2/library/os.html#os.walk

Et le module tarfile: http://docs.python.org/2/library/tarfile.html#tar-examples

0
guettli

Je pense que vous aurez peut-être intérêt à utiliser l'option -exec de find.

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

Find exécute ensuite la commande à l'aide d'un appel système, de sorte que les espaces et les nouvelles lignes soient préservés (au lieu d'un tube, ce qui nécessiterait la citation de caractères spéciaux). Notez que "tar -c" fonctionne que l'archive existe ou non, et que (du moins avec bash) ni {} ni + ne doivent être cités.

0
Drake Clarris