78 votes

Capturer la sortie de find. -print0 dans un tableau bash

À l'aide de find . -print0 semble être le seul moyen sûr d'obtenir une liste de fichiers dans un bash en raison de la possibilité de noms de fichiers contenant des espaces, retours à la ligne, guillemets, etc.

Cependant, je vais avoir un moment difficile en fait de faire trouver de la sortie de l'utile à l'intérieur de bash ou avec d'autres utilitaires de ligne de commande. Le seul moyen que j'ai réussi à faire usage de la sortie est par la tuyauterie à perl, et l'évolution de perl FI à null:

find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;'

Cet exemple imprime le nombre de fichiers trouvés, en évitant le danger de retours à la ligne dans les noms de fichiers de corrompre le comte, comme ce serait le cas avec:

find . | wc -l

Comme la plupart des programmes en ligne de commande ne prennent pas en charge null délimité par l'entrée, je me dis que la meilleure chose serait de capturer la sortie de l' find . -print0 dans un bash tableau, comme je l'ai fait dans le perl extrait ci-dessus, puis continuer avec la tâche, quelle qu'elle soit.

Comment puis-je faire cela?

Cela ne fonctionne pas:

find . -print0 | ( IFS=$'\0' ; array=( $( cat ) ) ; echo ${#array[@]} )

Beaucoup plus générale, la question pourrait être: Comment puis-je faire des choses utiles avec des listes de fichiers en bash?

105voto

Gordon Davisson Points 22534

Sans vergogne de vol à partir de Greg BashFAQ:

unset a i
while IFS= read -r -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done < <(find /tmp -type f -print0)

Notez que la redirection de construire utilisé ici (cmd1 < <(cmd2)) est similaire, mais pas tout à fait la même que la plus habituelle pipeline (cmd2 | cmd1) -- si les commandes sont shell objets internes (par exemple, while), le pipeline version exécute en sous-coquille, et toutes les variables qu'ils définie (par exemple, la matrice a) sont perdus lors de leur sortie. cmd1 < <(cmd2) ne fonctionne cmd2 dans un shell interne est exécuté, de sorte que la matrice de vie passé sa construction. Avertissement: ce type de redirection est uniquement disponible en bash, pas même bash, sh-mode d'émulation; vous devez lancer votre script avec #!/bin/bash.

Aussi, parce que le fichier de traitement de l'étape (dans ce cas, il suffit d' a[i++]="$file", mais vous voulez peut-être faire quelque chose de fantaisiste directement dans la boucle) a son entrée redirigé, il ne peut pas utiliser toutes les commandes qui peuvent lire depuis l'entrée standard. Pour éviter cette limitation, j'ai tendance à utiliser:

unset a i
while IFS= read -r -u3 -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

...qui passe de la liste de fichiers via l'unité 3, plutôt que de stdin.

7voto

Balázs Pozsár Points 1004

Peut-être que vous recherchez xargs:

 find . -print0 | xargs -r0 do_something_useful
 

L'option -L 1 pourrait également vous être utile, ce qui permet à xargs d'exécuter do_something_useful avec un seul argument de fichier.

5voto

zstegi Points 79

Le principal problème est, que le délimiteur NUL (\0) est inutile ici, car il n'est pas possible d'attribuer FI un NUL-valeur. De sorte que de bons programmeurs nous nous occupons, que l'entrée de notre programme est quelque chose qu'il est capable de gérer.

Nous avons d'abord créer un petit programme, qui ne cette partie pour nous:

#!/bin/bash
printf "%s" "$@" | base64

...et de l'appeler base64str (n'oubliez pas de chmod +x)

Deuxièmement, nous pouvons maintenant utiliser une simple boucle for:

for i in `find -type f -exec base64str '{}' \;`
do 
  file="`echo -n "$i" | base64 -d`"
  # do something with file
done

Donc, l'astuce est que, en base64 chaîne n'a pas de signe qui entraîne des problèmes pour bash - bien sûr, un xxd ou quelque chose de semblable peut aussi faire le travail.

4voto

Voici un essai sur la façon de gérer correctement les noms de fichiers dans un shell, avec beaucoup de détails:

http://www.dwheeler.com/essays/filenames-in-shell.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X