2
0

More than 3 years have passed since last update.

Go のディレクトリ操作まわりの関数

Posted at

今回のトピックも自分のメモの色合いが強い。今回これを書いている理由は、「記憶するため」の一つの手段だ。

今回のお題はとても簡単で

ディレクトリを捜査して、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 があれば後は簡単にファイル操作出来そうやな。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0