godirwalkは、ファイルシステム上のディレクトリツリーを横断するライブラリです。
要するに、なぜこのライブラリを作成したのですか?
filepath.Walkよりも速いです。filepath.Walkよりも正しいです。filepath.Walkよりも使いやすいです。filepath.Walkよりも柔軟です。特定の状況に応じて、GOでファイルをウォーキングするためにライブラリが必要になる場合があります。
追加の例はexamples/サブディレクトリに記載されています。
このライブラリは、最初の引数でfilepath.Clean呼び出すことにより、OS固有のパスセパレーターに基づいて提供されたトップレベルのディレクトリ名を正規化します。ただし、提供されたコールバック関数を呼び出すときに、正しいOS固有のパスセパレーターを使用して作成されたパス名を常に提供します。
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)
})このライブラリは、ファイルシステムディレクトリツリーを通過するための関数を提供するだけでなく、通常、 os.ReadDirまたはos.ReadDirnamesを使用するよりもはるかに速く、特定のディレクトリの即時子孫のリストを取得するための機能を提供します。
filepath.Walk 、 os.ReadDir 、およびos.ReadDirnamesを好んでgodirwalkを使用する理由は次のとおりです。
filepath.Walkよりも速いですベンチマークのfilepath.Walkと比較すると、UNIX findユーティリティの速度に匹敵する速度で、ダーウィンの速度の5〜10倍の速度を実行することが観察されています。 Linuxの速度の約2倍。 Windowsの速度の約4倍。
このパフォーマンスがどのように向上しますか?ほぼ同じ出力を提供するための作業は少なくなります。このライブラリは、作業を行うために同じsyscall関数を呼び出しますが、呼び出しが少なく、必要な情報を捨てず、オペレーティングシステムからファイルシステムエントリデータを読み取るたびに新しいバッファーを再編成するのではなく、ディレクトリから読み取りのために同じスクラッチバッファーを再利用することで、途中でメモリの解約が少なくなります。
ファイルシステムディレクトリツリーを通過している間、 filepath.Walkディレクトリの即時子孫のリストを取得し、ノードの名前に付属のオペレーティングシステムによって提供されるファイルシステムエントリのノードタイプ情報を削除します。次に、コールバック関数を呼び出す直前に、 filepath.Walk各ノードのos.Statを呼び出し、返されたos.FileInfo情報をコールバックに渡します。
os.Statが提供するos.FileInfo情報は非常に役立ち、 os.FileModeデータを含むこともありますが、各ノードに追加のシステム呼び出しが必要です。
ほとんどのコールバックはノードタイプが何であるかだけを気にするため、このライブラリはタイプ情報を捨てるのではなく、 os.FileMode値の形式でコールバック関数にその情報を提供します。このライブラリが提供する提供されたos.FileMode値には、ノードタイプの情報のみがあり、ファイルのモードからの許可ビット、スティッキービット、またはその他の情報がないことに注意してください。コールバックが特定のノードのos.FileInfoデータ構造全体を気にしている場合、コールバックは必要に応じて、必要に応じてos.Statをeasiy cain eved case case case case case case case case case case case nectedにします。
$ 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.Walkよりも正しいです私も以前にこれについて気にしませんでしたが、ユーモアをしました。私たちは皆、一度書いてどこでも走ることができる方法が大好きです。言語の採用、成長、および成功には、GOがサポートするすべてのアーキテクチャおよびオペレーティングシステムで作成されていないソフトウェアを実行できます。
Traversed File Systemがディレクトリへのシンボリックリンクによって引き起こされる論理ループを持っている場合、Unix filepath.Walkはシンボリックリンクを無視し、エラーなしでディレクトリツリー全体を通過します。ただし、Windowsでは、 filepath.Walk 、ディレクトリのシンボリックリンクを追跡し続けます。最終的にはfilepath.Walkが早期終了し、パス名の無限リンクの無限のループを連結してパス名が長すぎるとエラーを返します。このエラーは、Windowsからfilepath.Walkを通過し、 filepath.Walkを実行しているアップストリームクライアントに届きます。
要点は、どのプラットフォームfilepath.Walkが実行されているかによって、行動が異なるということです。これは明らかに意図的ではありませんが、標準ライブラリで修正されるまで、互換性の問題を提示します。
このライブラリは、上記の問題を修正し、UNIXまたはWindowsの論理ファイルSytemループに従わないようにします。さらに、 FollowSymbolicLinks Trueに設定されている場合にのみ、シンボリックリンクに従います。 Windowsやその他のオペレーティングシステムでの動作は同一です。
filepath.Walkよりも使いやすいですこのライブラリは、信じられないほどよく書かれたfilepath.Walk Standard Libraryの動作を模倣するよう努めていますが、より簡単または直感的な発信者インターフェイスを提供するために少し逸脱する場所があります。
このライブラリは、遭遇するすべてのファイルシステムノードでos.Statを呼び出しないため、コールバック関数がフィルタリングする可能性のあるエラーイベントはありません。 filepath.WalkFunc関数の署名の3番目の引数os.Statからコールバック関数にエラーを渡すための署名はもはや必要ではないため、このライブラリからのコールバック関数の署名から排除されます。
さらに、 filepath.WalkFuncとこのライブラリのWalkFuncの間のこのわずかなインターフェースの違いは、 filepath.Walkを使用するときにコールバックハンドラーが記述する必要があるボイラープレートコードを排除します。それに応じて渡されて分岐するエラー値をチェックする必要があるすべてのコールバック関数ではなく、このライブラリのユーザーは、コールバック関数に入力するとすぐに確認するエラー値さえありません。これは、ランタイムパフォーマンスとコードの明確さの両方の改善です。
すべてのOSプラットフォームでfilepath.Walk 、SOLIDUS( / )区切りパス名でコールバック関数を呼び出します。対照的に、このライブラリは、OS固有のパスネームセパレーターでコールバックを呼び出し、 filepath.Cleanを削除して、指定されたパス名を実際に使用する前に、各ノードのコールバック関数のクリーンを削除します。
言い換えれば、Windowsでさえ、 filepath.Walk 、 some/path/to/foo.txtでコールバックを呼び出し、指定されたファイルを使用する前に、すべてのファイルのパス名の正規化を実行する必要があります。これは、真にOS不可知論のコールバック関数を作成するための隠されたボイラープレート要件です。実際、多くのクライアントはUNIXで開発され、Windowsでテストされていないクライアントはこの繊細さを無視し、誰かがWindowsでそのソフトウェアを実行しようとするとソフトウェアのバグが発生します。
このライブラリは、Windowsで実行するときに同じファイルに対してsomepathtofoo.txtを使用してコールバック関数を呼び出し、クライアントによるパス名を正規化する必要性を排除し、クライアントがWindowsではなくUNIXで動作する可能性を軽減します。
この拡張機能は、このライブラリのランタイムパフォーマンスを改善しながら、コールバック関数のさらに多くのボイラープレートコードの必要性を排除します。
godirwalk.SkipThis 、 filepath.SkipDirよりも使用するのが直感的ですこのライブラリがエミュレートしなければならないfilepath.WalkFuncインターフェイスの間違いなく混乱する側面の1つは、発信者がファイルシステムエントリをスキップするためにWalk機能を指示する方法です。 filepath.WalkとこのライブラリのWalkの両方で、コールバック関数がディレクトリをスキップし、子供に降りないようにすると、 filepath.SkipDir返します。コールバック関数がfilepath.SkipDir非ディレクトリ、 filepath.Walkに返す場合、このライブラリは現在のディレクトリのエントリの処理を停止します。これは必ずしもほとんどの開発者が望んだり期待したりするものではありません。特定の非ディレクトリエントリを単にスキップするだけでなく、ディレクトリ内のエントリの処理を継続する場合は、コールバック関数がnilを返す必要があります。
このインターフェイス設計の意味は、ファイルシステムの階層を歩き、エントリをスキップする場合、ノードがどのタイプのファイルシステムエントリであるかに基づいて異なる値を返す必要があります。エントリをスキップするには、エントリがディレクトリの場合は、 filepath.SkipDir返す必要があり、エントリがディレクトリでない場合は、 nil返す必要があります。これは、単に直感的なインターフェイスではないという理由だけで、多くの開発者が苦労していることを観察した不幸なハードルです。
filepath.WalkFuncインターフェイスに付着するコールバック関数の例は、特定のサブストリングであるoptSkipを含むフルパス名が含まれるファイルシステムエントリをスキップさせます。このライブラリは、コールバック関数がfilepath.SkipDir返すとき、 filepath.Walkの同一の動作をサポートしていることに注意してください。
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
}このライブラリは、新しいトークンエラー値であるSkipThisを提供することにより、コールバック関数に必要なロジックボイラープレートの一部を排除しようとします。現在のエントリがディレクトリである場合、コールバックがfilepath.SkipDir返したかのように、その子供は列挙されません。現在のエントリが非ディレクトリの場合、現在のディレクトリの次のファイルシステムエントリが列挙されますnil次の例のコールバック関数は、以前と同一の動作を持っていますが、ボイラープレートが少なく、確かに、より簡単にフォローできる論理があります。
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よりも柔軟ですこのライブラリのデフォルトの動作は、 filepath.Walkがそうであるように、ディレクトリツリーを歩くときにディレクトリへのシンボリックリンクを無視することです。ただし、シンボリックリンクを含む、見つかった各ノードでコールバック関数を呼び出します。ディレクトリツリーを通過するときに特定のユースケースがシンボリックリンクに従うために存在する場合、このライブラリは、 FollowSymbolicLinks configパラメーターをtrueに設定することにより、そのように呼び出すことができます。
このライブラリのデフォルトの動作は、 filepath.Walkがそうであるように、各ノードにアクセスする前に、常にディレクトリの直接の子孫を並べ替えることです。これは通常、望ましい動作です。ただし、これは、ディレクトリノードに多くのエントリがある場合、名前を並べ替えるために必要なわずかなパフォーマンスとメモリのペナルティが必要です。さらに、Callerが構成パラメーターでUnsorted列挙を指定した場合、発信者がエントリを消費するにつれて読み取りディレクトリがゆっくりと実行されます。ノードにアクセスする前にディレクトリの直接の子孫を並べ替える必要がない特定のユースケースが存在する場合、このライブラリは、 Unsortedパラメーターがtrueに設定されているときにソートステップをスキップします。
これは、非決定的な順序でファイルシステムの階層を横断する潜在的なハザードの興味深い読み物です。あなたが解決している問題が注文ファイルの影響を受けていないことがわかっている場合は、 Unsortedされます。それ以外の場合は、このオプションの設定をスキップします。
研究者は、Pythonスクリプトのバグが何百もの研究に影響を与えた可能性があることを発見しました
このライブラリは、子供が処理された後、各ディレクトリに呼び出されるコールバック関数を指定する機能を上流コードに提供します。これは、ファイルシステムをより効率的に通過した後、空のディレクトリを再帰的に削除するために使用されています。この使用法の例についてはexamples/clean-emptiesディレクトリを参照してください。
このライブラリは、オペレーティングシステムが戻るエラーに対して呼び出されるコールバックを指定する機能を上流コードに提供し、上流コードが階層の歩行を停止するかどうかを停止するかどうか、エラーコールバックが提供されないか、エラーを発生させるノードをスキップするかどうかを決定できるようにします。この使用法の例についてはexamples/walk-fastディレクトリを参照してください。