Il semble qu'il find
faudrait de toute façon vérifier si un chemin donné correspond à un fichier ou un répertoire afin de parcourir récursivement le contenu des répertoires.
Voici une motivation et ce que j'ai fait localement pour me convaincre que c'est find . -type f
vraiment plus lent que find .
. Je n'ai pas encore fouillé dans le code source de GNU find.
Je sauvegarde donc certains fichiers de mon $HOME/Workspace
répertoire et j'exclus les fichiers qui sont soit des dépendances de mes projets, soit des fichiers de contrôle de version.
J'ai donc exécuté la commande suivante qui s'est exécutée rapidement
% find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > ws-files-and-dirs.txt
find
canalisé à grep
peut - être mauvaise forme, mais il semblait que la façon la plus directe d'utiliser un filtre regex niée.
La commande suivante inclut uniquement les fichiers dans la sortie de find et a pris sensiblement plus de temps.
% find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > ws-files-only.txt
J'ai écrit du code pour tester les performances de ces deux commandes (avec dash
et tcsh
, juste pour exclure tout effet que le shell pourrait avoir, même s'il ne devrait pas y en avoir). Les tcsh
résultats ont été omis car ils sont essentiellement les mêmes.
Les résultats que j'ai obtenus montrent une pénalité de 10% -type f
Voici la sortie du programme indiquant le temps nécessaire pour exécuter 1000 itérations de diverses commandes.
% perl tester.pl
/bin/sh -c find Workspace/ >/dev/null
82.986582
/bin/sh -c find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
90.313318
/bin/sh -c find Workspace/ -type f >/dev/null
102.882118
/bin/sh -c find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
109.872865
Testé avec
% find --version
find (GNU findutils) 4.4.2
Copyright (C) 2007 Free Software Foundation, Inc.
Sur Ubuntu 15.10
Voici le script Perl que j'ai utilisé pour l'analyse comparative
#!/usr/bin/env perl
use strict;
use warnings;
use Time::HiRes qw[gettimeofday tv_interval];
my $max_iterations = 1000;
my $find_everything_no_grep = <<'EOF';
find Workspace/ >/dev/null
EOF
my $find_everything = <<'EOF';
find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
EOF
my $find_just_file_no_grep = <<'EOF';
find Workspace/ -type f >/dev/null
EOF
my $find_just_file = <<'EOF';
find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
EOF
my @finds = ($find_everything_no_grep, $find_everything,
$find_just_file_no_grep, $find_just_file);
sub time_command {
my @args = @_;
my $start = [gettimeofday()];
for my $x (1 .. $max_iterations) {
system(@args);
}
return tv_interval($start);
}
for my $shell (["/bin/sh", '-c']) {
for my $command (@finds) {
print "@$shell $command";
printf "%s\n\n", time_command(@$shell, $command);
}
}
la source
find
faudrait de toute façon vérifier si un chemin donné correspond à un fichier ou un répertoire afin de parcourir récursivement le contenu des répertoires. - il devrait vérifier s'il s'agit d'un répertoire, il ne devrait pas vérifier s'il s'agit d'un fichier. Il existe d'autres types d'entrées: canaux nommés, liens symboliques, blocs périphériques spéciaux, sockets ... Donc, même s'il a peut-être déjà vérifié si c'est un répertoire, cela ne signifie pas qu'il sait s'il s'agit d'un fichier normal.-type f
et sans lui. Mais au début, le noyau Linux l'a chargé dans le cache et la toute première découverte a été plus lente.-type f
option provoquait l'find
appelstat()
oufstat()
ou quoi que ce soit afin de savoir si le nom du fichier correspondait à un fichier, un répertoire, un lien symbolique, etc. J'ai fait unstrace
sur unfind .
et unfind . -type f
et la trace était presque identique, ne différant que par leswrite()
appels contenant des noms de répertoire. Donc, je ne sais pas, mais je veux connaître la réponse.time
commande intégrée pour voir combien de temps une commande prend pour s'exécuter, vous n'aviez pas vraiment besoin d'écrire un script personnalisé pour tester.Réponses:
GNU find a une optimisation qui peut être appliquée
find .
mais pasfind . -type f
: s'il sait qu'aucune des entrées restantes dans un répertoire n'est un répertoire, alors il ne prend pas la peine de déterminer le type de fichier (avec l'stat
appel système) à moins que l'un des les critères de recherche l'exigent. L'appelstat
peut prendre un temps mesurable car les informations se trouvent généralement dans l'inode, dans un emplacement séparé sur le disque, plutôt que dans le répertoire conteneur.Comment le sait-il? Parce que le nombre de liens sur un répertoire indique le nombre de sous-répertoires qu'il possède. Sur les systèmes de fichiers Unix typiques, le nombre de liens d'un répertoire est de 2 plus le nombre de répertoires: un pour l'entrée du répertoire dans son parent, un pour l'
.
entrée et un pour l'..
entrée dans chaque sous-répertoire.L'
-noleaf
option indique defind
ne pas appliquer cette optimisation. Ceci est utile sifind
est invoqué sur un système de fichiers où le nombre de liens de répertoire ne suit pas la convention Unix.la source
find
source, il utilise simplement les appelsfts_open()
etfts_read()
.stat
appels quand elle n'en a pas besoin en raison du nombre de liens de répertoire, et l'-noleaf
option est documentée dans le manuel.stat
même dans lafts...
version - il passe le drapeau approprié pour cela à l'fts_open
appel. Mais ce que je ne suis pas sûr est toujours pertinent, c'est la vérification du nombre de liens. Il vérifie à la place si l'enregistrement fts retourné possède l'un des drapeaux "répertoire". Il se peut quefts_read
lui - même vérifie les liens pour définir cet indicateur, maisfind
ne le fait pas. Vous pouvez voir si votre version repose surfts
en appelantfind --version
.find
théoriquement capable de déterminer quand toutes les entrées d'un répertoire sont également des répertoires et d'utiliser cette information?