Noms de fichiers avec des espaces brisés, boucle de recherche

26

J'ai un script qui recherche tous les fichiers de plusieurs sous-dossiers et archive dans 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 de recherche 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?

Mettre à 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

Le résultat obtenu 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
    
posée Ubuntuser 10.09.2013 - 11:33
la source

9 réponses

31

Utiliser for avec find est une mauvaise approche ici, voir par exemple ceci écrivez sur la boîte de Pandore que vous ouvrez.

L'approche recommandée consiste à utiliser find , while et read comme décrit ci-après, . Vous trouverez ci-dessous un exemple qui devrait fonctionner pour vous:

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

Ainsi, vous délimitez les noms de fichiers avec des caractères nuls ( find ), cela signifie que la variation d'espace et d'autres caractères spéciaux ne poseront aucun problème.

Pour mettre à jour une archive avec les fichiers que tar localise, vous pouvez passer sa sortie directement à tar :

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

Notez que vous n'avez pas à différencier si l'archive existe ou non, -printf la manipulera avec discernement. Notez également l'utilisation de ./ ici pour éviter d'inclure le bit %code% dans l'archive.

    
réponse donnée Thor 10.09.2013 - 12:38
la source
11

Essayez de citer la boucle for comme suit:

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

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

Essayez également de définir

IFS=$'\n'
    
réponse donnée kiri 10.09.2013 - 11:38
la source
6

Cela fonctionne et est plus simple:

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

Merci à Rupa ( lien ) pour cette réponse.

    
réponse donnée ShawnMilo 17.07.2014 - 23:11
la source
4

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 $'
   -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.
' file; do something with "$file" done < <(find . -type f -name '*.*' -print0)

Ceci devrait gérer tous les noms de fichiers compatibles POSIX - voir man find

while read -rd $'
   -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.
' file; do something with "$file" done < <(find . -type f -name '*.*' -print0)
    
réponse donnée steeldriver 10.09.2013 - 14:14
la source
1
find . <find arguments> -print0 | xargs -0 grep <pattern>
    
réponse donnée user2802945 27.05.2015 - 23:32
la source
1

J'ai fait quelque chose comme ceci 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

a travaillé comme un charme:)

    
réponse donnée Scott B 18.05.2016 - 16:15
la source
0

La plupart des réponses ici se cassent s'il y a un caractère de nouvelle ligne dans le nom du fichier. J'utilise la bash depuis plus de 15 ans, mais uniquement interactive.

En Python, vous pouvez nous utiliser os.walk (): lien

Et le module tarfile: lien

    
réponse donnée guettli 10.09.2013 - 13:20
la source
0

Je pense que vous feriez peut-être mieux d'utiliser l'option -exec de find .

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

Rechercher 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 (plutôt un canal, 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.

    
réponse donnée Drake Clarris 10.09.2013 - 14:02
la source
-1

Comme suggéré par minerz029, vous devez citer l’extension de la commande find . Vous devez également indiquer toutes les substitutions de $FILE dans votre boucle.

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

Notez que la syntaxe $() devrait être préférée à l'utilisation de backticks; voir cette U & L question . J'ai également supprimé le mot clé [[ et je l'ai remplacé par la commande [ car il s'agit de POSIX.

    
réponse donnée Joseph R. 10.09.2013 - 11:55
la source

Lire d'autres questions sur les étiquettes