今回のトピックも自分のメモの色合いが強い。今回これを書いている理由は、「記憶するため」の一つの手段だ。
今回のお題はとても簡単で
ディレクトリを捜査して、Dockerfile にマッチするものを見つけてそのディレクトリにあるメタデータやディレクトリの名前を取得したいというとっても簡単なもの。問題は私が Go でどう書くかを記憶していないことにある。そこでブログを書いてみて整理してみよう。
Current Directry
currentDir := os.Getwd()
ファイルパスの結合
ファイルパスの結合はプラットフォームによって異なるので、自分で足すと暗黒。
Join のパラメータは、...string
であるので可変長である。リストにも簡単に変換できる。
path := filepath.Join(currentDir, "tests", "images)
ちなみに、...
は Ellipsis という。[]string
から Ellipsis に変換したいケースは
param := []string{ CurrentDir, "tests", "images" }
path := filepath.Join(param...)
としてやると良い。 ちなみに、`filepath.Join(CurrentDir, param...) は動作しない。Join のパラメータはあくまで Ellipsis だけだからだ。
ディレクトリの検索を行う。
ディレクトリのサーチを行う場合、主に3つの方法がある。
ディレクトリのリストを取得する
特定のディレクトリのリストの一覧を取得する。
dirs, err := ioutil.ReadDir(path)
だ。ちなみに戻り値は、FileInfo
構造体でこんな値がある。
// A FileInfo describes a file and is returned by Stat.
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
Glob
パターンマッチで検索をしたい場合は、Glob を使うことが出来る。
target := filepath.Join(path, "*/Dockerfile"
dirs := filepath.Glob(target)
最初は、複数のディレクトリを挟んでもマッチしてくれるのかと期待したが、単なる1階層のマッチの様子だ。
WalkDir 再帰的に検索する
最後のパターンが、WalkDir
だ。これは再帰的に検索をしてくれる。Dirといいつつ、ファイルも検索してくれる。ちなみに、Walk
という関数もあるが、これは、使わなくてよい。go 1.16 から、WalkDir が導入されて、こちらの方が効率が良いらしい。
見ての通り、関数を渡すと、ディレクトリもしくはファイルを見つけるとコールしてくれる。
ちなみに、filepath.Base()
は、ファイル名のフルパスで、最後の要素(ここではファイル名のみ)を返してくれる。
err = filepath.WalkDir(currentDir, func(path string, d fs.DirEntry, err error) error {
if !d.Dir() {
fmt.Printf("Base: %ss\n", filepath.Base(path))
if d.Name() == "Dockerfile" {
dockerfile, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
}
}
return err
}
DirEntry は、ファイル/ディレクトリ名のほかに、ディレクトリかどうかとか、FileModeとか、FileInfoを返してくれる。のでとても便利っぽい。
// A DirEntry is an entry read from a directory
// (using the ReadDir function or a ReadDirFile's ReadDir method).
type DirEntry interface {
// Name returns the name of the file (or subdirectory) described by the entry.
// This name is only the final element of the path (the base name), not the entire path.
// For example, Name would return "hello.go" not "/home/gopher/hello.go".
Name() string
// IsDir reports whether the entry describes a directory.
IsDir() bool
// Type returns the type bits for the entry.
// The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method.
Type() FileMode
// Info returns the FileInfo for the file or subdirectory described by the entry.
// The returned FileInfo may be from the time of the original directory read
// or from the time of the call to Info. If the file has been removed or renamed
// since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist).
// If the entry denotes a symbolic link, Info reports the information about the link itself,
// not the link's target.
Info() (FileInfo, error)
}
実行結果
Base: go.mod
Base: main.go
Base: activator_test.go
Base: httpoptions_test.go
Base: test_images.go
Base: apps.go
Base: command.go
Base: helm.go
Base: kubectl.go
Base: shared.go
Base: system.go
Base: kafka_test.go
Base: .dockerignore
Base: .gitignore
Base: Dockerfile
おなじみの、ioutil.ReadFile()
と、 json.Unmarshall
があれば後は簡単にファイル操作出来そうやな。