🌱

Goでcpコマンド的なの作ってみた🌱

2023/04/09に公開

Goでcpみたいなものを作ってみました

この記事は初心者🌱の自分のためのメモに近いです。ご容赦を。
paizaで学習するときに、いちいちディレクトリを複製するのが手間だったのでディレクトリの複製がしたかった。
ちょうどGoでなんでもいいからツールを作ってみたかったから作った。
<s>cpなんていう便利なものがあったことは知らなかった</s>

主に使用したパッケージ

どれも標準パッケージですね

  • io
  • os
  • path/filepath

今回のリポジトリ

https://github.com/takaryo1010/copyDirectory

面白かったところ

os.Argsについて

これを使用することによって、go run 以降の引数を取得できる。
go run . filenameだと os.Argsの要素の0番目が.なのでファイルのパス。1番目がfilenameとなる。
os.Argsの要素の1番目がある場合のみプログラムを続けたかったので、以下のように書いた。

if len(os.Args) == 2 {
		directory_name = os.Args[1]
	} else {
		os.Exit(1)
	}

osパッケージまわりについて

os.Statは返り値にfs.FileInfoerrorを持つ。
fs.FileInfoとはなんぞや。

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() any           // underlying data source (can return nil)
}

ということらしい。
ぱっと見で理解できそうなのは、NameSizeくらいかな...
と思ってたらIsDirが目についた。
これ、ディレクトリかそうじゃないかboolで返してくれるじゃん。天才じゃん。
ってことで利用する。
osパッケージにはディレクトリ作成の関数と、ファイル作成の関数が別で用意されているので、IsDirを使って区別できた。

filepath.Walk()について

func Walk(root string, fn WalkFunc) error
rootには探索したいディレクトリを入れる。
fnWalkFuncってなに。

type WalkFunc func(path string, info fs.FileInfo, err error) error

ってことらしい。
おそらく、 filepath.Walk()君が、WalkFunc君の引数にそれぞれのディレクトリ等のパスとかinfoを投げて、それらを利用した関数を書けるということだと思う。
実際に書いてみた。

func make_directory_or_file(filename string) {
	var new_path string
	filepath.Walk("original_directory", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		new_path = strings.Replace(path, "original_directory", filename, -1)
		if info.IsDir() {
			err := os.MkdirAll(new_path, 0755)
			if err != nil {
				log.Println(err)
				os.Exit(1)
			}
		} else {
			dst, err := os.Create(new_path)
			if err != nil {
				log.Println(err)
				os.Exit(1)
			}
			src, err := os.Open(path)
			if err != nil {
				log.Println(err)
				os.Exit(1)
			}
			_, err = io.Copy(dst, src)
			if err != nil {
				log.Println(err)
				os.Exit(1)
			}
		}
		return nil
	})

}

これはさっきのosパッケージも使って書いてみた。
わりかしコンパクトに書けたのではないかと思いたい。

ひとりごと

Walkで思い出したけど、これはおそらくグラフ理論の木構造とか使ってんのかなぁみたいな。
静的解析ツールを学習する機会があったけどおそらく今回のWalkと似たことをやっていたんだと思う。抽象構文木をつかって、要素を一つ一つに関数を実行してたんだね。今やっと理解できた。ってことで多少は理解が進んだから静的解析ツールの話も記事にまとめます。
今回の学習を先にしていればもっと理解が早かったのかあ…

感想

cpコマンドとほとんど同じことをやってるので便利なツールができた!みたいな感じにはならなかったけど得たものが凄い多かった。
こういう感じで簡単なものでもツールいっぱい作ってみようと思う。

Discussion