Go言語_ホワイトリスト方式のファイル削除

ファイル削除

ホワイトリストファイルに記載したものは削除しない、という方式の削除プログラムです。


ホワイトリスト

ホワイトリストは下記の形式になります。

アスタリスクをつけると、その子要素は削除しないという指定になります。


node_modules/jquery/dist/*
node_modules/jquery/LICENSE.txt

プログラム


package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

/*!
 * @brief      ホワイトリストに含まれるか判定する
 * @param[in]  filePath   操作対象のファイルパス
 * @param[in]  whiteList  ホワイトリスト情報
 * @return     true or false
 */
func isWhiteFile(filePath string, whiteFile string) bool {
	// 完全一致の判定
        if filePath == whiteFile {
		return true
	}

        // 親階層の判定
        if strings.HasPrefix(whiteFile, filePath) {
		return true
        }

	// アスタリスク指定は正規表現で判定する
	if strings.Contains(whiteFile, "*") {
	    reg := regexp.MustCompile(whiteFile)
	    return reg.MatchString(filePath)
	}

	return false
}

/*!
 * @brief      削除してよいか判定する
 * @param[in]  filePath   操作対象のファイルパス
 * @param[in]  whiteList  ホワイトリスト情報
 * @return     true or false
 */
func isNeedDelete(filePath string, whiteList []string) bool {
	for _, whiteFile := range whiteList {
		if isWhiteFile(filePath, whiteFile) {
			return false
		}
	}
	return true
}

/*!
 * @brief     ファイルを削除する
 * @param[in] filePath   変更対象のディレクトリ名
 * @param[in] whiteList  ホワイトリスト情報
 * @return    error      エラー情報
 */
func deleteFile(filePath string, whiteList []string) error {
        // 削除対象であるか判定する
	if !isNeedDelete(filePath, whiteList) {
	        return nil
	}

        // ファイルを削除する
        if err := os.Remove(filePath); err != nil {
	        return err
        }

	return nil
}

/*!
 * @brief     ディレクトリ名を再帰的に変更する
 * @param[in] rootDir   トップディレクトリ名
 * @param[in] whiteList ホワイトリスト情報
 * @return    error     エラー情報
 */
func deleteFileRecursive(rootDir string, whiteList []string) error {

	files, err := ioutil.ReadDir(rootDir)
	if err != nil {
		return err
	}

	for _, file := range files {
		fullPath := filepath.Join(rootDir, file.Name())

		// ディレクトリならば再帰する
		if file.IsDir() {
			err = deleteFileRecursive(fullPath, whiteList)
			if err != nil {
				return err
			}
		}

		err = deleteFile(fullPath, whiteList)
		if err != nil {
			return err
		}
	}
	return nil
}

/*!
 * @brief  オプション解析を行う。
 * @retval string  ターゲットとなるディレクトリ名
 * @retval string  ホワイトリスト用ファイルパス
 * @retval error   エラー情報
 */
func analyzeOption() (string, string, error) {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTION] DIR:\n",
			filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}

	optF := flag.String("f", "", "white list filepath")
	flag.Parse()

	if flag.NArg() != 1 {
		flag.Usage()
		return "", "", errors.New("Error: Invalid arguments.")
	}

	return (flag.Args())[0], *optF, nil
}

/*!
 * @brief  ホワイトリストを読み込みスライスに格納する。
 * @param[in] filename   ファイルパス
 * @retval    []string   ファイル内容
 * @retval    error      エラー情報
 */
func readWhiteList(filename string) ([]string, error) {
	// ファイルを開く
	fd, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer fd.Close() // return時に閉じる

	// Scannerで読み込む
	lines := make([]string, 0, 128)
	scanner := bufio.NewScanner(fd)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if serr := scanner.Err(); serr != nil {
		return nil, serr
	}

	return lines, nil
}

/*!
 * @brief  初期化処理
 * @retval string ディレクトリ名
 * @retval string ファイル名
 * @retval error  エラー情報
 */
func initialize() (string, []string, error) {
	// オプション解析
	target, filename, err := analyzeOption()
	if err != nil {
		return "", nil, err
	}

	// ホワイトリストが指定されていれば読み込む
	var whiteList []string
	if filename != "" {
		whiteList, err = readWhiteList(filename)
		if err != nil {
			return "", nil, err
		}
	}

	return target, whiteList, nil
}

/*!
 * @brief   main routine
 * @return  成功時には0を返し、失敗時には1を返す。
 */
func main() {
	target, whiteList, err := initialize()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	err = deleteFileRecursive(target, whiteList)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	os.Exit(0)
}

実行

npmインストールしたモジュールの内、不要なものを削除する場合に用います。


go run deleter.go -f whitelist.conf node_modules/

関連ページ