概要
最近業務であるディレクトリの中身を丸ごとコピーしてたのですが、様々なサイトを見ても処理が分かりにくいものが多かったので今一度分かりやすいコードを書きたいと思います。
他の参考サイトを見ても分かりにくいと感じた点はおそらく以下の点です。
- 再帰処理の複雑さにより、各関数の役割が明確で無くなっている。
参考サイトによっては「ディレクトリを追加しながらファイルを読む関数」等があったりと個人的には分かりにくく、実際に業務においてレビューするのであれば指摘をするかなと。
環境
今回私が実行した環境は以下です。
- Go 1.16.8
- VSCode
- Windows10
- WSL2(Ubuntu)
- Docker Desktop
Windows
の Docker Desktop
を起動し、 Ubuntu
上でコンテナを立ち上げ、 VSCode
のリモートコンテナより実行しました。
便利な世の中になりましたね。
なお、これから説明する完成したコードは以下に全て配置しています。
実装
では実装したコードの概要、実際のコードと見ていきます。
処理概要
大まかな処理の内容は以下となります。
func entry() {
// 探索を行い、その探索した結果によって関数を振り分けるエントリ
return nil
}
func addDir() {
// ディレクトリ作成処理を行う
// エントリ追加処理を行う
return
}
func addFile() {
// ファイル追加処理を行う
return
}
entry
関数は追加されたエントリをひたすら分析する関数です。
また、 addDir
関数は新しく追加されたディレクトリやファイルに対してエントリの追加を行う関数です。
addFile
はファイルを追加する関数です。ファイルを追加するだけであるため、特にエントリを追加する必要はありません。
だいぶスッキリしてるのではないでしょうか。
処理コード
全体のコードです。
言語に関わらず同じように記載出来ると思いますが、ここでは簡単に書ける Go
言語で書いてみます。
package main
import (
"io/ioutil"
"os"
"path/filepath"
)
func main() {
src := "./input"
dest := "./output"
err := entry(src, dest, "")
if err != nil {
os.Exit(1)
}
}
func entry(src, dest, path string) error {
sp := filepath.Join(src, path)
fs, err := os.Stat(sp)
if err != nil {
return err
}
if fs.IsDir() { // ディレクトリの場合
if err := addDir(src, dest, path); err != nil {
return err
}
} else { // ファイルの場合
if err := addFile(src, dest, path); err != nil {
return err
}
}
return nil
}
func addDir(src, dest, path string) error {
dp := filepath.Join(dest, path)
if err := os.Mkdir(dp, 0700); err != nil {
return err
}
sp := filepath.Join(src, path)
fi, err := os.ReadDir(sp)
if err != nil {
return err
}
for _, f := range fi {
err := entry(sp, dp, f.Name())
if err != nil {
return err
}
}
return nil
}
func addFile(src, dest, path string) error {
d, err := ioutil.ReadFile(filepath.Join(src, path))
if err != nil {
return err
}
df, err := os.Create(filepath.Join(dest, path))
if err != nil {
return err
}
defer df.Close()
if _, err = df.Write(d); err != nil {
return err
}
return nil
}
おわりに
もし他に分かりやすい書き方あれば教えてほしいです。