目录操作 #

目录是组织文件的重要方式。Go语言提供了完整的目录操作API,包括创建、删除、遍历等功能。

创建目录 #

创建单个目录 #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Mkdir("mydir", 0755)
    if err != nil {
        fmt.Println("创建目录失败:", err)
        return
    }
    fmt.Println("目录创建成功")
}

创建多级目录 #

使用 MkdirAll 可以一次性创建多级目录:

go
package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.MkdirAll("path/to/my/dir", 0755)
    if err != nil {
        fmt.Println("创建目录失败:", err)
        return
    }
    fmt.Println("多级目录创建成功")
}

提示: MkdirAll 是幂等的,如果目录已存在,不会返回错误。

删除目录 #

删除空目录 #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Remove("mydir")
    if err != nil {
        fmt.Println("删除失败:", err)
        return
    }
    fmt.Println("目录删除成功")
}

递归删除目录 #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.RemoveAll("path/to/my/dir")
    if err != nil {
        fmt.Println("删除失败:", err)
        return
    }
    fmt.Println("目录及其内容已删除")
}

警告: RemoveAll 会删除目录及其所有内容,请谨慎使用!

读取目录内容 #

使用 ReadDir #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    entries, err := os.ReadDir(".")
    if err != nil {
        fmt.Println("读取目录失败:", err)
        return
    }
    
    for _, entry := range entries {
        typeStr := "文件"
        if entry.IsDir() {
            typeStr = "目录"
        }
        
        info, _ := entry.Info()
        fmt.Printf("%-20s %s (%d 字节)\n", entry.Name(), typeStr, info.Size())
    }
}

使用 ReadDir 返回的 DirEntry #

DirEntry 接口提供了目录条目的基本信息:

go
package main

import (
    "fmt"
    "os"
)

func main() {
    entries, _ := os.ReadDir(".")
    
    for _, entry := range entries {
        fmt.Println("名称:", entry.Name())
        fmt.Println("是否为目录:", entry.IsDir())
        
        info, err := entry.Info()
        if err == nil {
            fmt.Println("大小:", info.Size())
            fmt.Println("模式:", info.Mode())
        }
        fmt.Println("---")
    }
}

遍历目录树 #

使用 filepath.WalkDir #

filepath.WalkDir 是遍历目录树的推荐方法:

go
package main

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

func main() {
    root := "."
    
    err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        
        indent := ""
        depth := len(filepath.SplitList(path))
        for i := 0; i < depth; i++ {
            indent += "  "
        }
        
        typeStr := "📄"
        if d.IsDir() {
            typeStr = "📁"
        }
        
        fmt.Printf("%s%s %s\n", indent, typeStr, d.Name())
        return nil
    })
    
    if err != nil {
        fmt.Println("遍历错误:", err)
    }
}

查找特定文件 #

go
package main

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

func findFiles(root, ext string) []string {
    var files []string
    
    filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        
        if !d.IsDir() && strings.HasSuffix(d.Name(), ext) {
            files = append(files, path)
        }
        return nil
    })
    
    return files
}

func main() {
    goFiles := findFiles(".", ".go")
    
    fmt.Println("Go源文件:")
    for _, f := range goFiles {
        fmt.Println(" ", f)
    }
}

跳过特定目录 #

go
package main

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

func main() {
    skipDirs := map[string]bool{
        ".git":   true,
        "vendor": true,
        "node_modules": true,
    }
    
    filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        
        if d.IsDir() && skipDirs[d.Name()] {
            fmt.Println("跳过目录:", d.Name())
            return filepath.SkipDir
        }
        
        fmt.Println(path)
        return nil
    })
}

获取当前目录 #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    dir, err := os.Getwd()
    if err != nil {
        fmt.Println("获取当前目录失败:", err)
        return
    }
    fmt.Println("当前目录:", dir)
}

切换工作目录 #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    originalDir, _ := os.Getwd()
    fmt.Println("原始目录:", originalDir)
    
    err := os.Chdir("/tmp")
    if err != nil {
        fmt.Println("切换目录失败:", err)
        return
    }
    
    newDir, _ := os.Getwd()
    fmt.Println("新目录:", newDir)
    
    os.Chdir(originalDir)
}

创建临时目录 #

go
package main

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

func main() {
    dir, err := os.MkdirTemp("", "myapp-")
    if err != nil {
        fmt.Println("创建临时目录失败:", err)
        return
    }
    
    fmt.Println("临时目录:", dir)
    
    defer os.RemoveAll(dir)
    
    file, _ := os.Create(filepath.Join(dir, "temp.txt"))
    file.WriteString("临时文件内容")
    file.Close()
    
    fmt.Println("临时文件已创建")
}

实用示例:目录大小统计 #

go
package main

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

func dirSize(path string) (int64, error) {
    var size int64
    
    err := filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        
        if !d.IsDir() {
            info, err := d.Info()
            if err != nil {
                return err
            }
            size += info.Size()
        }
        return nil
    })
    
    return size, err
}

func formatSize(bytes int64) string {
    const (
        KB = 1024
        MB = KB * 1024
        GB = MB * 1024
    )
    
    switch {
    case bytes >= GB:
        return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB))
    case bytes >= MB:
        return fmt.Sprintf("%.2f MB", float64(bytes)/float64(MB))
    case bytes >= KB:
        return fmt.Sprintf("%.2f KB", float64(bytes)/float64(KB))
    default:
        return fmt.Sprintf("%d B", bytes)
    }
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("用法: go run main.go <目录>")
        return
    }
    
    dir := os.Args[1]
    size, err := dirSize(dir)
    if err != nil {
        fmt.Println("错误:", err)
        return
    }
    
    fmt.Printf("目录 %s 大小: %s\n", dir, formatSize(size))
}

实用示例:目录同步复制 #

go
package main

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

func copyDir(src, dst string) error {
    info, err := os.Stat(src)
    if err != nil {
        return err
    }
    
    if err := os.MkdirAll(dst, info.Mode()); err != nil {
        return err
    }
    
    entries, err := os.ReadDir(src)
    if err != nil {
        return err
    }
    
    for _, entry := range entries {
        srcPath := filepath.Join(src, entry.Name())
        dstPath := filepath.Join(dst, entry.Name())
        
        if entry.IsDir() {
            if err := copyDir(srcPath, dstPath); err != nil {
                return err
            }
        } else {
            if err := copyFile(srcPath, dstPath); err != nil {
                return err
            }
        }
    }
    
    return nil
}

func copyFile(src, dst string) error {
    srcFile, err := os.Open(src)
    if err != nil {
        return err
    }
    defer srcFile.Close()
    
    srcInfo, _ := srcFile.Stat()
    
    dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcInfo.Mode())
    if err != nil {
        return err
    }
    defer dstFile.Close()
    
    _, err = io.Copy(dstFile, srcFile)
    return err
}

func main() {
    if len(os.Args) < 3 {
        fmt.Println("用法: go run main.go <源目录> <目标目录>")
        return
    }
    
    src := os.Args[1]
    dst := os.Args[2]
    
    err := copyDir(src, dst)
    if err != nil {
        fmt.Println("复制失败:", err)
        return
    }
    
    fmt.Println("目录复制成功!")
}

小结 #

函数 用途
os.Mkdir 创建单个目录
os.MkdirAll 创建多级目录
os.Remove 删除空目录或文件
os.RemoveAll 递归删除目录
os.ReadDir 读取目录内容
filepath.WalkDir 遍历目录树
os.Getwd 获取当前工作目录
os.Chdir 切换工作目录
os.MkdirTemp 创建临时目录

目录操作是文件系统编程的基础,合理使用这些API可以高效地管理文件和目录结构。