1.
Le premier:
for f in *; do
echo "$f"
done
échoue pour les fichiers appelés -n, -eet des variantes comme -neneet avec quelques déploiements bash, avec les noms de fichiers contenant des barres obliques inverses.
La deuxième:
find * -prune | while read f; do
echo "$f"
done
échoue pour les cas encore plus (fichiers appelés !, -H, -name, (, noms de fichiers qui commencent ou se terminent par des blancs ou contiennent des caractères ... saut de ligne)
C'est le shell qui se développe *, findne fait qu'imprimer les fichiers qu'il reçoit comme arguments. Vous pourriez aussi bien avoir utilisé à la printf '%s\n'place ce qui, comme il printfest intégré, éviterait également l' erreur potentielle de trop d'arguments .
2.
L'expansion de *est triée, vous pouvez la rendre un peu plus rapide si vous n'avez pas besoin du tri. Dans zsh:
for f (*(oN)) printf '%s\n' $f
ou simplement:
printf '%s\n' *(oN)
bashn'a pas d'équivalent pour autant que je sache, vous devez donc y recourir find.
3.
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(ci-dessus en utilisant une -print0extension non standard GNU / BSD ).
Cela implique toujours de générer une commande find et d'utiliser une while readboucle lente , donc ce sera probablement plus lent que d'utiliser la forboucle à moins que la liste des fichiers ne soit énorme.
4.
De plus, contrairement à l'expansion du shell, les caractères génériques findeffectueront un lstatappel système sur chaque fichier, il est donc peu probable que le non-tri compense cela.
Avec GNU / BSD find, cela peut être évité en utilisant leur -maxdepthextension qui déclenchera une optimisation en sauvant lstat:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Parce que findcommence la sortie des noms de fichiers dès qu'il les trouve (à l'exception de la mise en mémoire tampon de sortie stdio), où cela peut être plus rapide, c'est si ce que vous faites dans la boucle prend du temps et la liste des noms de fichiers est plus qu'un tampon stdio (4 / 8 kB). Dans ce cas, le traitement dans la boucle commencera avant d' findavoir fini de trouver tous les fichiers. Sur les systèmes GNU et FreeBSD, vous pouvez utiliser stdbufpour que cela se produise plus tôt (désactivation de la mise en mémoire tampon stdio).
5.
La manière POSIX / standard / portable d'exécuter des commandes pour chaque fichier avec findest d'utiliser le -execprédicat:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Dans le cas de echocela, c'est moins efficace que de faire une boucle dans le shell car le shell aura une version intégrée de echowhile finddevra générer un nouveau processus et l'exécuter /bin/echopour chaque fichier.
Si vous devez exécuter plusieurs commandes, vous pouvez faire:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Mais attention, elle cmd2n'est exécutée qu'en cas de cmd1succès.
6.
Une manière canonique d'exécuter des commandes complexes pour chaque fichier est d'appeler un shell avec -exec ... {} +:
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
Cette fois-ci, nous sommes redevenus efficaces echocar nous shutilisons celui intégré à et la -exec +version apparaît le moins shpossible.
7.
Dans mes tests sur un répertoire avec 200.000 fichiers avec des noms courts sur ext4, zshcelui (paragraphe 2.) est de loin le plus rapide, suivi de la première for i in *boucle simple (bien que comme d'habitude, bashest beaucoup plus lente que les autres shells pour cela).
findn'ouvre pas les fichiers qu'il trouve. La seule chose que je peux voir vous mordre ici en ce qui concerne un grand nombre de fichiers est ARG_MAX .