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
Utiliser for
avec find
est 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 find
name__, while
et read
comme 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 find
localise, vous pouvez passer sa sortie directement à tar
name__:
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, tar
la gérera de manière judicieuse. Notez également l'utilisation de -printf
ici pour éviter d'inclure le bit ./
dans l'archive.
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'
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.
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.
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 :)
find . <find arguments> -print0 | xargs -0 grep <pattern>
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
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.