ioutil.ReadDirでディレクトリ内を走査した時にシンボリックリンクがあった場合、LStatとStatのどちらでFileInfoが返ってくるか分からなかったため試してみた。
testディレクトリ内に別のディレクトリに対してシンボリックリンクを張ったものを置いてあります。
package main import ( "fmt" "io/ioutil" "os" "path/filepath" ) func main() { currentDir, err := os.Getwd() if err != nil {} dirPath := filepath.Join(currentDir, "test") infos, err := ioutil.ReadDir(dirPath) for _, info := range infos { fmt.Println("IsDir: ", info.IsDir()) } }
これで、実行結果が true ならリンク先を辿っているStatであり、 false ならリンク先を辿らないLstatになります。
ʕ ◔ϖ◔ʔ % go run main.go IsDir: false
おっ、これはLstatの動きですね。
一応 ioutil.ReadDir のコードも追っかけていきましょう。
Goの ioutil.ReadDir は内部的にはソート機能のついた os.File.Readdir で、そちらのコードを眺めてみると。
func (f *File) readdir(n int) (fi []FileInfo, err error) {
dirname := f.name
if dirname == "" {
dirname = "."
}
names, err := f.Readdirnames(n)
fi = make([]FileInfo, 0, len(names))
for _, filename := range names {
fip, lerr := lstat(dirname + "/" + filename)
if IsNotExist(lerr) {
// File disappeared between readdir + stat.
// Just treat it as if it didn't exist.
continue
}
if lerr != nil {
return fi, lerr
}
fi = append(fi, fip)
}
if len(fi) == 0 && err == nil && n > 0 {
// Per File.Readdir, the slice must be non-empty or err
// must be non-nil if n > 0.
err = io.EOF
}
return fi, err
}
真ん中くらいで lstat 使ってますね。
もし、「ディレクトリ内を走査しつつ、シンボリックリンクだった場合はリンク先を辿る」といった処理を実現したい場合には、以下のようにする感じですかね。
package main import ( "fmt" "io/ioutil" "os" "path/filepath" ) func main() { currentDir, err := os.Getwd() if err != nil {} dirPath := filepath.Join(currentDir, "test") infos, err := ioutil.ReadDir(dirPath) for _, info := range infos { filePath := filepath.Join(dirPath, info.Name()) i, err := os.Stat(filePath) if err != nil {} fmt.Println("IsDir: ", i.IsDir()) } }
これを実行すると
ʕ ◔ϖ◔ʔ % go run main.go IsDir: true
おっ、ちゃんとシンボリックリンクのリンク先を辿れてますね。
面倒なのでStatに対応した ioutil.ReadDir か os.File.Readdir があればいいんですけどねぇ・・・
現場からは以上です。