0
0

filepath.WalkDirでディレクトリ内を削除するときには注意

Posted at

やりたいこと

処理が終わった一時ディレクトリ内を綺麗したい。
でも、親ディレクトリは残しておきたい。
ディレクトリ内は階層が統一されていない。

削除したいディレクトリ構造

.
├── parentDir1
│   ├── ChildDirA
│   │   └── ChildDirAA
│   └── ChildDirB
└── parentDir2

試したこと

2つの方法で実施

  • ReadDir
  • filepath.WalkDir
    filepath.WalkDirのほうが処理速度が早いという記事情報あり。

ReadDirでディレクトリ内を取得

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

func main() {
	dir := "your_directory_path" // 対象のディレクトリパスをここに指定

	err := removeSubdirectories(dir)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Println("All subdirectories removed successfully.")
	}
}

func removeSubdirectories(dir string) error {
	// ディレクトリ内の全てのファイルとサブディレクトリを取得
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, file := range files {
		if file.IsDir() {
			// サブディレクトリのフルパスを取得
			subDirPath := filepath.Join(dir, file.Name())
            fmt.Printf("Removing directory: %s\n", subDirPath)
			// サブディレクトリを削除
			err = os.RemoveAll(subDirPath)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

結果

Removing directory: /parentDir1/ChildDirA
Removing directory: /parentDir1/ChildDirB
All subdirectories removed successfully.

正常に終了。

filepath.WalkDirで取得

package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	dir := "your_directory_path" // 対象のディレクトリパスをここに指定

	err := removeSubdirectoriesUsingWalkDir(dir)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Println("All subdirectories removed successfully.")
	}
}

func removeSubdirectoriesUsingWalkDir(root string) error {
	return filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return err
		}

		// ルートディレクトリそのものは削除しない
		if path == root {
			return nil
		}

		// ディレクトリであれば削除
		if d.IsDir() {
			fmt.Printf("Removing directory: %s\n", path)
			return os.RemoveAll(path)
		}

		return nil
	})
}

結果

Removing directory: /parentDir1/ChildDirA
Error: open /parentDir1/ChildDirA: no such file or directory

エラー
/parentDir1/ChildDirAディレクトリがないというエラーが出力されています。
/parentDir1/ChildDirAは削除されていますが、
/parentDir1/ChildDirBは削除されていません。

削除部分を以下に変更してみる

削除を実行しないで、nilを返す

// ディレクトリであれば削除
if d.IsDir() {
    fmt.Printf("Removing directory: %s\n", path)
    //return os.RemoveAll(path)
    return nil
}

結果

Removing directory: /parentDir1/ChildDirA
Removing directory: /parentDir1ChildDirA/ChildDirAA
Removing directory: /parentDir1/ChildDirB
All subdirectories removed successfully.

どうやら、ChildDirAを削除した後に子ディレクトリを削除しようとしてエラーになっている。
削除の前にディレクトリの存在確認を追加してみる。

func removeSubdirectoriesUsingWalkDir(root string) error {
	return filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			// ディレクトリの存在確認
			if _, err := os.Stat(path); os.IsNotExist(err) {
				fmt.Printf("Directory does not exist: %s\n", path)
				return nil
			}
			return err
		}

		// ルートディレクトリそのものは削除しない
		if path == root {
			return nil
		}

		// ディレクトリであれば削除
		if d.IsDir() {
			fmt.Printf("Removing directory: %s\n", path)
			return os.RemoveAll(path)
		}

		return nil
	})
}

結果

Removing directory: /parentDir1/ChildDirA
Directory does not exist: /parentDir1/ChildDirA
Removing directory: /parentDir1/ChildDirB
Directory does not exist: /parentDir1/ChildDirB
All subdirectories removed successfully.

ディレクトリ内の削除は完了
なぜか/parentDir1/ChildDirAと/parentDir1/ChildDirBのそれぞれで存在確認エラー
子ディレクトリを削除するが、その後に子ディレクトリ内の探索が行われる
その為、エラーとなる。

解決方法

filepath.WalkDirでディレクトリ内の探索時、エラーの判定後にエラーの種別が存在確認エラーの場合は無視する

0
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
0
0