はじめに
こんにちは、Go界の三船敏郎です。一昔前はPythonのキラースニペットコードといえばまず間違いなくos.path.walk() (今はos.walk())を使ったコードが出たわけですが、GoにもWalk()関数がありました。でもちょっと使い方でハマったのでメモ。
やりたいこと
あるディレクトリ以下のファイルに対して何かをした結果を取得したい。
困ること
Walk()の中の各パスに対するハンドラのfilepath.WalkFuncはerrorしか返さない型になってる。
func Walk(root string, walkFn WalkFunc) error
type WalkFunc func(path string, info os.FileInfo, err error) error
結論
クロージャ使え。
サンプル
たとえばこんな階層構造で、Walkしながらegg/spamを読み込んだ結果を取得したいとする。
├── egg │ ├── ham │ └── spam └── spam ├── bar └── foo
このときspamは次のような内容となっている。
% cat egg/spam Monty Python
- main.go
package main import ( "fmt" "io/ioutil" "os" "path/filepath" ) func readSpamUnderEgg(buf *[]byte) filepath.WalkFunc { return func(path string, f os.FileInfo, err error) error { if filepath.Base(path) == "egg" && f.IsDir() { *buf, err = ioutil.ReadFile(filepath.Join(path, "spam")) if err != nil { return err } } return nil } } func main() { pwd, err := os.Getwd() if err != nil { fmt.Errorf("%v\n", err) } var buf = []byte("hello") fmt.Println(string(buf)) // "hello"になる err = filepath.Walk(pwd, readSpamUnderEgg(&buf)) // 参照渡ししないとだめ if err != nil { fmt.Errorf("%v\n", err) } fmt.Println(string(buf)) // "Monty Python"になってほしい }
実行すると
% go run main.go hello Monty Python
できた