godirwalk est une bibliothèque pour traverser un arbre de répertoire sur un système de fichiers.
En bref, pourquoi ai-je créé cette bibliothèque?
filepath.Walk .filepath.Walk .filepath.Walk .filepath.Walk .Selon vos circonstances spécifiques, vous n'aurez peut-être plus besoin d'une bibliothèque pour le fichier en marche.
Des exemples supplémentaires sont fournis dans les examples/ sous-répertoire.
Cette bibliothèque normalisera le nom du répertoire de haut niveau fourni en fonction du séparateur de chemin spécifique à l'OS en appelant filepath.Clean sur son premier argument. Cependant, il fournit toujours le nom de chemin créé en utilisant le séparateur de chemin spécifique à l'OS correct lors de l'invoquer la fonction de rappel fournie.
dirname := "some/directory/root"
err := godirwalk . Walk ( dirname , & godirwalk. Options {
Callback : func ( osPathname string , de * godirwalk. Dirent ) error {
// Following string operation is not most performant way
// of doing this, but common enough to warrant a simple
// example here:
if strings . Contains ( osPathname , ".git" ) {
return godirwalk . SkipThis
}
fmt . Printf ( "%s %s n " , de . ModeType (), osPathname )
return nil
},
Unsorted : true , // (optional) set true for faster yet non-deterministic enumeration (see godoc)
}) Cette bibliothèque fournit non seulement des fonctions pour traverser une arborescence de répertoire de système de fichiers, mais également pour obtenir une liste de descendants immédiats d'un répertoire particulier, généralement beaucoup plus rapidement que l'utilisation os.ReadDir ou os.ReadDirnames .
Voici pourquoi j'utilise godirwalk de préférence à filepath.Walk , os.ReadDir et os.ReadDirnames .
filepath.Walk Comparé à filepath.Walk dans les repères, il a été observé qu'il se déroule entre cinq et dix fois la vitesse sur Darwin, à des vitesses comparables à celles de l'Unix find Utility; et environ deux fois la vitesse sur Linux; et environ quatre fois la vitesse sur les fenêtres.
Comment obtient-il cette augmentation des performances? Cela fonctionne moins pour vous donner presque la même sortie. Cette bibliothèque appelle les mêmes fonctions syscall pour faire le travail, mais elle fait moins d'appels, ne jette pas d'informations dont elle pourrait avoir besoin, et crée moins de désabonnement de mémoire en cours de réutilisation du même tampon à gratter pour lire à partir d'un répertoire plutôt que de réaffecter un nouveau tampon à chaque fois qu'il lit les données de saisie du système de fichiers à partir du système d'exploitation.
Lors de la traversée d'une arborescence du répertoire du système de fichiers, filepath.Walk obtient la liste des descendants immédiats d'un répertoire et jette les informations de type de nœud pour l'entrée du système de fichiers qui est fournie par le système d'exploitation qui vient avec le nom du nœud. Ensuite, immédiatement avant d'invoquer la fonction de rappel, filepath.Walk invoque os.Stat pour chaque nœud et transmet les informations os.FileInfo renvoyées au rappel.
Bien que les informations os.FileInfo fournies par os.Stat soient extrêmement utiles - et incluent même les données os.FileMode - la fourniture nécessite un appel système supplémentaire pour chaque nœud.
Étant donné que la plupart des rappels ne se soucient que du type de nœud, cette bibliothèque ne jette pas les informations de type, mais fournit plutôt ces informations à la fonction de rappel sous la forme d'une valeur os.FileMode . Notez que la valeur os.FileMode fournie que cette bibliothèque fournit uniquement les informations de type de nœud et ne dispose pas des bits d'autorisation, des bits collants ou d'autres informations du mode du fichier. Si le rappel se soucie de l'ensemble de la structure de données os.FileInfo d'un nœud particulier, le rappel peut invoquer os.Stat en cas de besoin, et uniquement en cas de besoin.
$ go test -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: github.com/karrick/godirwalk
BenchmarkReadDirnamesStandardLibrary-12 50000 26250 ns/op 10360 B/op 16 allocs/op
BenchmarkReadDirnamesThisLibrary-12 50000 24372 ns/op 5064 B/op 20 allocs/op
BenchmarkFilepathWalk-12 1 1099524875 ns/op 228415912 B/op 416952 allocs/op
BenchmarkGodirwalk-12 2 526754589 ns/op 103110464 B/op 451442 allocs/op
BenchmarkGodirwalkUnsorted-12 3 509219296 ns/op 100751400 B/op 378800 allocs/op
BenchmarkFlameGraphFilepathWalk-12 1 7478618820 ns/op 2284138176 B/op 4169453 allocs/op
BenchmarkFlameGraphGodirwalk-12 1 4977264058 ns/op 1031105328 B/op 4514423 allocs/op
PASS
ok github.com/karrick/godirwalk 21.219s$ go test -bench=. -benchmem
goos: linux
goarch: amd64
pkg: github.com/karrick/godirwalk
BenchmarkReadDirnamesStandardLibrary-12 100000 15458 ns/op 10360 B/op 16 allocs/op
BenchmarkReadDirnamesThisLibrary-12 100000 14646 ns/op 5064 B/op 20 allocs/op
BenchmarkFilepathWalk-12 2 631034745 ns/op 228210216 B/op 416939 allocs/op
BenchmarkGodirwalk-12 3 358714883 ns/op 102988664 B/op 451437 allocs/op
BenchmarkGodirwalkUnsorted-12 3 355363915 ns/op 100629234 B/op 378796 allocs/op
BenchmarkFlameGraphFilepathWalk-12 1 6086913991 ns/op 2282104720 B/op 4169417 allocs/op
BenchmarkFlameGraphGodirwalk-12 1 3456398824 ns/op 1029886400 B/op 4514373 allocs/op
PASS
ok github.com/karrick/godirwalk 19.179sfilepath.WalkJe ne m'en souciais pas auparavant non plus, mais je m'humais. Nous aimons tous comment nous pouvons écrire une fois et courir partout. Il est essentiel pour l'adoption, la croissance et le succès de la langue, que le logiciel que nous créons peut être non modifié sur toutes les architectures et systèmes d'exploitation pris en charge par Go.
Lorsque le système de fichiers traversé a une boucle logique causée par des liens symboliques vers les répertoires, sur Unix filepath.Walk ignore les liens symboliques et traverse l'intégralité de l'arborescence du répertoire sans erreur. Cependant, sur Windows, filepath.Walk continuera à suivre les liens symboliques du répertoire, même s'il n'est pas censé, ce qui a finalement fait fin filepath.Walk à se terminer tôt et à renvoyer une erreur lorsque le chemin de chemin devient trop long de la concaténation de boucles sans fin de liens symboliques sur le piste. Cette erreur provient de Windows, passe via filepath.Walk et vers le client en amont exécutant filepath.Walk .
Le point à retenir est que le comportement est différent en fonction de la plate-forme filepath.Walk est en cours d'exécution. Bien que cela ne soit clairement pas intentionnel, jusqu'à ce qu'il soit fixé dans la bibliothèque standard, il présente un problème de compatibilité.
Cette bibliothèque résout le problème ci-dessus de sorte qu'il ne suivra jamais les boucles de système de fichiers logiques sur UNIX ou Windows. En outre, il ne suivra que des liens symboliques lorsque FollowSymbolicLinks est défini sur true. Le comportement sur les fenêtres et autres systèmes d'exploitation est identique.
filepath.Walk Bien que cette bibliothèque s'efforce d'imiter le comportement de la bibliothèque standard filepath.Walk .
Étant donné que cette bibliothèque n'invoque pas os.Stat sur chaque nœud de système de fichiers qu'il rencontre, il n'y a pas d'événement d'erreur possible pour que la fonction de rappel puisse filtrer. Le troisième argument de la signature de la fonction filepath.WalkFunc pour passer l'erreur de os.Stat à la fonction de rappel n'est plus nécessaire, et donc éliminé de la signature de la fonction de rappel de cette bibliothèque.
En outre, cette légère différence d'interface entre filepath.WalkFunc et WalkFunc de cette bibliothèque élimine le code de la baillit que les gestionnaires de rappel doivent écrire lorsqu'ils utilisent filepath.Walk . Plutôt que chaque fonction de rappel nécessitant de vérifier la valeur d'erreur qui y est transmise et de se ramifier en conséquence, les utilisateurs de cette bibliothèque n'ont même pas de valeur d'erreur à vérifier immédiatement après l'entrée dans la fonction de rappel. Il s'agit d'une amélioration à la fois dans les performances d'exécution et la clarté du code.
Sur chaque plate-forme OS, filepath.Walk invoque la fonction de rappel avec un nom de chemin délimité Solidus ( / ). En revanche, cette bibliothèque invoque le rappel avec le séparateur de chemin d'accès spécifique au système d'exploitation, évitant un appel à filepath.Clean dans la fonction de rappel pour chaque nœud avant d'utiliser réellement le nom fourni.
En d'autres termes, même sur Windows, filepath.Walk invoquera le rappel avec some/path/to/foo.txt , obligeant les clients bien écrits à effectuer une normalisation pathname pour chaque fichier avant de travailler avec le fichier spécifié. Il s'agit d'une exigence de chaudière cachée pour créer de véritables fonctions de rappel agnostiques OS. En vérité, de nombreux clients se sont développés sur UNIX et non testés sur Windows négligent cette subtilité, et entraîneront des bogues logiciels lorsque quelqu'un essaie d'exécuter ce logiciel sur Windows.
Cette bibliothèque invoque la fonction de rappel avec somepathtofoo.txt pour le même fichier lors de l'exécution sur Windows, en éliminant la nécessité de normaliser le chemin d'accès par le client et de réduire la probabilité qu'un client travaille sur Unix mais pas sur Windows.
Cette amélioration élimine la nécessité d'un code de chauffeur supplémentaire dans les fonctions de rappel tout en améliorant les performances d'exécution de cette bibliothèque.
godirwalk.SkipThis est plus intuitif à utiliser que filepath.SkipDir Un aspect sans doute déroutant de l'interface filepath.WalkFunc que cette bibliothèque doit imiter est la façon dont un appelant raconte la fonction Walk pour sauter les entrées du système de fichiers. Avec filepath.Walk et Walk de cette bibliothèque, lorsqu'une fonction de rappel veut sauter un répertoire et ne pas descendre dans ses enfants, il renvoie filepath.SkipDir . Si la fonction de rappel renvoie filepath.SkipDir pour un non-répertoire, filepath.Walk et cette bibliothèque cesseront de traiter d'autres entrées dans le répertoire actuel. Ce n'est pas nécessairement ce que la plupart des développeurs veulent ou attendent. Si vous souhaitez simplement sauter une entrée non répertoire particulière mais continuez à traiter les entrées dans le répertoire, la fonction de rappel doit renvoyer NIL.
Les implications de cette conception d'interface sont lorsque vous souhaitez parcourir une hiérarchie du système de fichiers et sauter une entrée, vous devez renvoyer une valeur différente en fonction du type de saisie du système de fichiers ce nœud. Pour sauter une entrée, si l'entrée est un répertoire, vous devez retourner filepath.SkipDir , et si l'entrée n'est pas un répertoire, vous devez retourner nil . Il s'agit d'un obstacle malheureux avec lequel j'ai observé de nombreux développeurs aux prises avec, tout simplement parce que ce n'est pas une interface intuitive.
Voici un exemple de fonction de rappel qui adhère à l'interface filepath.WalkFunc pour le faire sauter n'importe quelle entrée de système de fichiers dont le parcours complet comprend une sous-chaîne particulière, optSkip . Notez que cette bibliothèque prend toujours en charge le comportement identique de filepath.Walk lorsque la fonction de rappel renvoie filepath.SkipDir .
func callback1 ( osPathname string , de * godirwalk. Dirent ) error {
if optSkip != "" && strings . Contains ( osPathname , optSkip ) {
if b , err := de . IsDirOrSymlinkToDir (); b == true && err == nil {
return filepath . SkipDir
}
return nil
}
// Process file like normal...
return nil
} Cette bibliothèque tente d'éliminer une partie de ce chauffeur logique requise dans les fonctions de rappel en fournissant une nouvelle valeur d'erreur de jeton, SkipThis , qu'une fonction de rappel peut revenir pour sauter la saisie actuelle du système de fichiers quel que soit le type d'entrée. Si l'entrée actuelle est un répertoire, ses enfants ne seront pas énumérés, exactement comme si le rappel avait renvoyé filepath.SkipDir . Si l'entrée actuelle est un non-répertoire, la prochaine entrée de système de fichiers dans le répertoire actuel sera énumérée, exactement comme si le rappel renvoyé nil . L'exemple de fonction de rappel suivant a un comportement identique comme le précédent, mais a moins de chauffeur, et certes logique que je trouve plus simple à suivre.
func callback2 ( osPathname string , de * godirwalk. Dirent ) error {
if optSkip != "" && strings . Contains ( osPathname , optSkip ) {
return godirwalk . SkipThis
}
// Process file like normal...
return nil
}filepath.Walk Le comportement par défaut de cette bibliothèque consiste à ignorer les liens symboliques vers des répertoires lors de la marche d'un arbre d'annuaire, tout comme filepath.Walk le fait. Cependant, il invoque la fonction de rappel avec chaque nœud qu'il trouve, y compris les liens symboliques. Si un cas d'utilisation particulier existe pour suivre les liens symboliques lors de la traversée d'une arborescence d'annuaire, cette bibliothèque peut être invoquée de manière à le faire, en définissant le paramètre de configuration FollowSymbolicLinks sur true .
Le comportement par défaut de cette bibliothèque consiste à toujours trier les descendants immédiats d'un répertoire avant de visiter chaque nœud, tout comme filepath.Walk le fait. C'est généralement le comportement souhaité. Cependant, cela se présente à de légères pénalités de performances et de mémoire requises pour trier les noms lorsqu'un nœud de répertoire a de nombreuses entrées. De plus, si l'appelant spécifie l'énumération Unsorted dans le paramètre de configuration, les répertoires de lecture sont effectués paresseusement lorsque l'appelant consomme des entrées. Si un cas d'utilisation particulier existe, qui ne nécessite pas de tri les descendants immédiats du répertoire avant de visiter ses nœuds, cette bibliothèque ignorera l'étape de tri lorsque le paramètre Unsorted est défini sur true .
Voici une lecture intéressante des Hazzards potentiels de traverser une hiérarchie de système de fichiers dans un ordre non déterministe. Si vous savez que le problème que vous résolvez n'est pas affecté par les fichiers de commande sont visités, je vous encourage à utiliser Unsorted . Sinon, sautez cette option.
Les chercheurs trouvent que le bug dans le script Python peut avoir affecté des centaines d'études
Cette bibliothèque fournit du code en amont avec la possibilité de spécifier une fonction de rappel à invoquer pour chaque répertoire après le traitement de ses enfants. Cela a été utilisé pour supprimer récursivement les répertoires vides après avoir traversé le système de fichiers de manière plus efficace. Voir le répertoire examples/clean-empties pour un exemple de cette utilisation.
Cette bibliothèque fournit un code en amont avec la possibilité de spécifier un rappel à invoquer pour les erreurs que le système d'exploitation renvoie, permettant au code en amont de déterminer le plan suivant à prendre, de faire en sorte que la hiérarchie soit interrompue, comme il ne le ferait pas d'erreur fournie, ou de sauter le nœud qui a causé l'erreur. Voir les examples/walk-fast pour un exemple de cette utilisation.