首页 >后端开发 >Golang >整理您的桌面:在 Go 中构建文件管理器。

整理您的桌面:在 Go 中构建文件管理器。

Mary-Kate Olsen
Mary-Kate Olsen原创
2024-12-05 03:06:14534浏览

你的桌面混乱吗?您的桌面或下载目录中是否有各种各样的文件?让我们用一个简单的脚本来解决这个问题。

正如我们在任何 go 项目开始时所做的那样,我们使用“go mod init”指令生成 go.mod 文件。为了简单起见,我们将所有逻辑写入 main.go 文件中。

在编写任何代码之前,让我们先讨论一下我们希望脚本如何表现。我们希望能够将文件组织到指示文件类型或创建日期的目录中。简而言之,我们希望我们的脚本将视频文件分类到视频目录中,将音乐文件分类到音乐目录中,依此类推;或将在特定日期创建的所有文件排序到同一目录中。

现在让我们编码:

创建一个main.go文件并写入以下内容:

package main

type fileAnalyzer interface {
    analyzeAndSort() error
}

func analyze(fa fileAnalyzer) error {
    return fa.analyzeAndSort()
}

因为我们希望程序按照不同的标准对文件进行排序,所以我们创建了一个 fileAnalyzer 接口,它定义了一个方法:analyzeAndSort。
然后我们编写一个函数:analyze - 它将任何实现 fileAnalyzer 接口的结构作为参数并执行它的analyzeAndSort 方法。

在某些情况下,就像我们将在此程序中看到的那样,可能存在您不想移动的某些文件。例如,在测试脚本时,我们不希望程序将我们的 go 文件或可执行文件/二进制文件移动到另一个目录中。为了防止这种情况发生,我们必须创建一个黑名单,其中包括我们希望保持不变的所有文件。

在我们的 main.go 文件中,写入以下内容:

var blacklist = []string{
    "go",
    "mod",
    "exe",
    "ps1",
}

在这里,我添加了我希望保持未排序的文件的文件扩展名。 “.go”和“.mod”是 go 文件的扩展名。因为我使用 Windows 机器,所以我的二进制文件将以“.exe”作为扩展名。我还包含了“.ps1”,因为我喜欢在开发中使用 powershell 脚本 - 正如您将看到的。

接下来,我们编写一些辅助函数。

func getFileExtension(name string) string {
    return strings.TrimPrefix(filepath.Ext(name), ".")
}

func listFiles(dirname string) ([]string, error) {
    var files []string

    list, err := os.ReadDir(dirname)
    if err != nil {
        return nil, err
    }

    for _, file := range list {
        if !file.IsDir() {
            files = append(files, file.Name())
        }
    }

    return files, nil
}

func listDirs(dirname string) ([]string, error) {
    var dirs []string

    list, err := os.ReadDir(dirname)
    if err != nil {
        return nil, err
    }

    for _, file := range list {
        if file.IsDir() {
            dirs = append(dirs, file.Name())
        }
    }

    return dirs, nil
}

func mkdir(dirname string) error {
    err := os.Mkdir(dirname, 0644)

    if err != nil && os.IsExist(err) {
        return nil
    }

    var e *os.PathError

    if err != nil && errors.As(err, &e) {
        return nil
    }

    return err
}

func moveFile(name string, dst string) error {
    return os.Rename(name, filepath.Join(dst, name))
}

func getCurrentDate(t time.Time) string {
    return t.Format("2006-01-02")
}

func filter[T any](ts []T, fn func(T) bool) []T {
    filtered := make([]T, 0)

    for i := range ts {
        if fn(ts[i]) {
            filtered = append(filtered, ts[i])
        }
    }

    return filtered
}

其中大部分都是不言自明的,但我想谈谈“mkdir”函数。 “mkdir”函数创建一个目录,并将名称作为参数传递给它 - 但如果该目录已存在或存在“os.PathError”,该函数不会返回错误。

现在让我们创建一个实现 fileAnalyzer 接口的结构体:

package main

type fileAnalyzer interface {
    analyzeAndSort() error
}

func analyze(fa fileAnalyzer) error {
    return fa.analyzeAndSort()
}

fileTypeAnalyzer 结构有两个属性:wd,它保存当前工作目录的名称和映射器。映射器的键将是检测到的文件类型,而其值是与该键关联的文件扩展名列表。然后,我们创建一个构造函数并向映射器提供文件类型及其关联文件扩展名。请随意向列表中添加更多文件类型和扩展名。 anaylyzeAndSort 方法调用几个辅助函数和方法,将文件扩展名映射到文件类型,创建文件类型目录并将文件移动到所述目录中。我还添加了一个“misc”文件夹来保存映射器未捕获的文件 - 当然不包括列入黑名单的文件。

我们还希望能够按创建日期组织文件。让我们创建另一个实现 fileAnalyzer 接口但按日期组织文件的结构。

var blacklist = []string{
    "go",
    "mod",
    "exe",
    "ps1",
}

很多逻辑与 fileTypeAnalyzer 的逻辑相同。主要区别是我们不提供映射器 - 相反,我们从文件信息中获取创建日期并相应地创建目录。

现在让我们将所有内容放在我们的主函数中:

func getFileExtension(name string) string {
    return strings.TrimPrefix(filepath.Ext(name), ".")
}

func listFiles(dirname string) ([]string, error) {
    var files []string

    list, err := os.ReadDir(dirname)
    if err != nil {
        return nil, err
    }

    for _, file := range list {
        if !file.IsDir() {
            files = append(files, file.Name())
        }
    }

    return files, nil
}

func listDirs(dirname string) ([]string, error) {
    var dirs []string

    list, err := os.ReadDir(dirname)
    if err != nil {
        return nil, err
    }

    for _, file := range list {
        if file.IsDir() {
            dirs = append(dirs, file.Name())
        }
    }

    return dirs, nil
}

func mkdir(dirname string) error {
    err := os.Mkdir(dirname, 0644)

    if err != nil && os.IsExist(err) {
        return nil
    }

    var e *os.PathError

    if err != nil && errors.As(err, &e) {
        return nil
    }

    return err
}

func moveFile(name string, dst string) error {
    return os.Rename(name, filepath.Join(dst, name))
}

func getCurrentDate(t time.Time) string {
    return t.Format("2006-01-02")
}

func filter[T any](ts []T, fn func(T) bool) []T {
    filtered := make([]T, 0)

    for i := range ts {
        if fn(ts[i]) {
            filtered = append(filtered, ts[i])
        }
    }

    return filtered
}

我们配置一个记录器;获取当前工作目录作为参数传递给我们的 fileAnalyzer 实现;创建一个模式变量来保存作为标志传递给应用程序的值,并创建一个 switch 语句来控制我们想要如何排序。最后我们调用分析函数并将我们的 fileAnalyzer 实现作为参数传递。

仅此而已。让我们构建二进制文件并进行测试。我打电话给我的分拣员。你可以用“go build -o [name]”来称呼你的任何你想要的名字

这是一个散布着不同类型文件的文件夹:

Organize your desktop: Build a file organizer in Go.

让我们按文件类型进行组织:

Organize your desktop: Build a file organizer in Go.

Organize your desktop: Build a file organizer in Go.

让我们按文件创建日期进行组织:

Organize your desktop: Build a file organizer in Go.

Organize your desktop: Build a file organizer in Go.

作为奖励,如果您使用 Windows 计算机并且使用 powershell - 这里有一个脚本可以帮助您顺利测试您的程序。

创建一个task.ps1 文件并输入以下内容:

type fileTypeAnalyzer struct {
    wd     string
    mapper map[string][]string
}

func newFileTypeAnalyzer(wd string) *fileTypeAnalyzer {
    return &fileTypeAnalyzer{
        wd: wd,
        mapper: map[string][]string{
            "video":  {"mp4", "mkv", "3gp", "wmv", "flv", "avi", "mpeg", "webm"},
            "music":  {"mp3", "aac", "wav", "flac"},
            "images": {"jpg", "jpeg", "png", "gif", "svg", "tiff"},
            "docs":   {"docx", "csv", "txt", "xlsx"},
            "books":  {"pdf", "epub"},
        },
    }
}

func (f fileTypeAnalyzer) analyzeAndSort() error {
    files, err := listFiles(f.wd)
    if err != nil {
        return fmt.Errorf("could not list files: %w", err)
    }

    if err := f.createFileTypeDirs(files...); err != nil {
        return err
    }

    return f.moveFileToTypeDir(files...)
}

func (f fileTypeAnalyzer) moveFileToTypeDir(files ...string) error {
    dirs, err := listDirs(f.wd)
    if err != nil {
        return fmt.Errorf("could not list directories: %w", err)
    }

    for _, dir := range dirs {
        for _, file := range files {
            if slices.Contains(f.mapper[dir], strings.ToLower(getFileExtension(file))) {
                if err := moveFile(file, dir); err != nil {
                    return err
                }
            }
        }
    }

    files, err = listFiles(f.wd)
    if err != nil {
        return err
    }

    if len(files) == 0 {
        return nil
    }

    files = filter(files, func(f string) bool {
        return !slices.Contains(blacklist, getFileExtension(f))
    })

    for i := range files {
        if err := f.moveToMisc(files[i]); err != nil {
            return err
        }
    }

    return nil
}

func (f fileTypeAnalyzer) moveToMisc(file string) error {
    if err := mkdir("misc"); err != nil {
        return err
    }

    return moveFile(file, "misc")
}

func (f fileTypeAnalyzer) createFileTypeDirs(files ...string) error {
    for ftype, fvalues := range f.mapper {
        for _, file := range files {
            if slices.Contains(fvalues, getFileExtension(file)) {
                if err := mkdir(ftype); err != nil {
                    return fmt.Errorf("could not create folder: %w", err)
                }
            }
        }
    }

    return nil
}

使用脚本构建二进制文件:

Organize your desktop: Build a file organizer in Go.

使用脚本取消组织文件:

Organize your desktop: Build a file organizer in Go.

使用脚本删除目录:

Organize your desktop: Build a file organizer in Go.

以上是整理您的桌面:在 Go 中构建文件管理器。的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn