1.
Le premier:
for f in *; do
echo "$f"
done
échoue pour les fichiers appelés -n
, -e
et des variantes comme -nene
et 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 *
, find
ne 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 printf
est 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)
bash
n'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 -print0
extension non standard GNU / BSD ).
Cela implique toujours de générer une commande find et d'utiliser une while read
boucle lente , donc ce sera probablement plus lent que d'utiliser la for
boucle à moins que la liste des fichiers ne soit énorme.
4.
De plus, contrairement à l'expansion du shell, les caractères génériques find
effectueront un lstat
appel 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 -maxdepth
extension 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 find
commence 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' find
avoir fini de trouver tous les fichiers. Sur les systèmes GNU et FreeBSD, vous pouvez utiliser stdbuf
pour 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 find
est d'utiliser le -exec
prédicat:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Dans le cas de echo
cela, c'est moins efficace que de faire une boucle dans le shell car le shell aura une version intégrée de echo
while find
devra générer un nouveau processus et l'exécuter /bin/echo
pour chaque fichier.
Si vous devez exécuter plusieurs commandes, vous pouvez faire:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Mais attention, elle cmd2
n'est exécutée qu'en cas de cmd1
succè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 echo
car nous sh
utilisons celui intégré à et la -exec +
version apparaît le moins sh
possible.
7.
Dans mes tests sur un répertoire avec 200.000 fichiers avec des noms courts sur ext4, zsh
celui (paragraphe 2.) est de loin le plus rapide, suivi de la première for i in *
boucle simple (bien que comme d'habitude, bash
est beaucoup plus lente que les autres shells pour cela).
find
n'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 .