三十的博客

Go 中文件目录基本操作

本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
发布时间
最后更新
阅读量 加载中...

本文目标

相对路径时是基于执行时的工作目录,而不是代码文件位置

例如:

/home/user/projects/myproject/file/mian.go 存在代码 os.Create("./test.txt")

当你在 /home/user/projects/myproject/ 执行 file/mian.go 的程序时

会在 /home/user/projects/myproject/ 目录下创建 test.txt 文件

文件操作

创建文件

go
file, err := os.Create("./test.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

打开文件

go
file, err := os.Open("test.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

打开文件(带选项)

go
file, err := os.OpenFile("test.txt", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
    log.Fatal(err)
}
defer file.Close()

写入文件

go
content := []byte("Hello, World!\n")
_, err := file.Write(content)
if err != nil {
    log.Fatal(err)
}

读取文件

go
data := make([]byte, 100)
count, err := file.Read(data)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Read %d bytes: %q\n", count, data[:count])

文件信息

go
fileInfo, err := os.Stat("test.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println("File name:", fileInfo.Name())
fmt.Println("Size:", fileInfo.Size())
fmt.Println("Permissions:", fileInfo.Mode())
fmt.Println("Last modified:", fileInfo.ModTime())
fmt.Println("Is directory:", fileInfo.IsDir())

删除文件

go
err := os.Remove("test.txt")
if err != nil {
    log.Fatal(err)
}

目录操作

创建目录

go
err := os.Mkdir("mydir", 0755)
if err != nil {
    log.Fatal(err)
}

创建多级目录

go
err := os.MkdirAll("parent/child/grandchild", 0755)
if err != nil {
    log.Fatal(err)
}

读取目录

go
entries, err := os.ReadDir(".")
if err != nil {
    log.Fatal(err)
}

for _, entry := range entries {
    fmt.Println(entry.Name())
    if entry.IsDir() {
        fmt.Println("  (directory)")
    }
}

删除目录

go
err := os.Remove("mydir")
if err != nil {
    log.Fatal(err)
}
只会删除空目录,即使里面存在的是空文件。

删除目录及其内容

go
err := os.RemoveAll("parent")
if err != nil {
    log.Fatal(err)
}

不存在时创建目录

方法 1:使用 os.Stat+ os.Mkdir

go
func ensureDir(dirPath string) error {
    // 检查目录是否存在
    if _, err := os.Stat(dirPath); os.IsNotExist(err) {
        // 不存在则创建,0755 是目录权限(rwxr-xr-x)
        err := os.Mkdir(dirPath, 0755)
        if err != nil {
            return fmt.Errorf("创建目录失败: %v", err)
        }
    }
    return nil
}

方法 2:使用 os.MkdirAll(推荐)

go
func ensureDir(dirPath string) error {
    // MkdirAll 会创建所有不存在的父目录
    err := os.MkdirAll(dirPath, 0755)
    if err != nil {
        return fmt.Errorf("创建目录失败: %v", err)
    }
    return nil
}

路径操作

是开发跨平台文件系统相关程序的必备工具集。

虽然 os 包提供了一些路径操作,但更推荐使用 path/filepath 包。

常用方法

go
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath" // 提供跨平台的文件路径操作
	"strings"       // 提供字符串处理功能
)

func main() {
	// 获取当前工作目录

	// Getwd 返回当前进程的工作目录
	currentDir, err := os.Getwd()
	if err != nil {
		log.Fatal("获取当前目录失败:", err)
	}
	fmt.Println("当前工作目录:", currentDir)

	// 切换工作目录
	err = os.Chdir("../")

	currentDir, err = os.Getwd()
	if err != nil {
		log.Fatal("获取当前目录失败:", err)
	}
	fmt.Println("当前工作目录:", currentDir)

	// 跨平台路径拼接

	// Join 函数会根据操作系统自动使用正确的路径分隔符
	// 在 Unix 系统上是"/",在Windows上是"\"
	path := filepath.Join("dir1", "dir2", "wl.cool")
	fmt.Println("拼接后的路径:", path) // 输出取决于操作系统

	// 自动处理冗余路径符号

	// Join 会自动处理多余的路径分隔符和"."、".."
	fmt.Println("处理冗余分隔符:", filepath.Join("dir1//", "filename"))      // dir1/filename
	fmt.Println("处理相对路径:", filepath.Join("dir1/../dir1", "filename")) // dir1/filename

	// 获取路径组成要素

	// Dir 返回路径的目录部分(去掉最后一个元素)
	fmt.Println("目录部分:", filepath.Dir(path)) // dir1/dir2
	// Base 返回路径的最后一个元素
	fmt.Println("文件名部分:", filepath.Base(path)) // wl.cool

	// 判断绝对路径

	// IsAbs 判断路径是否是绝对路径
	fmt.Println("是否是绝对路径:", filepath.IsAbs("dir/file"))  // false
	fmt.Println("是否是绝对路径:", filepath.IsAbs("/dir/file")) // Unix: true, Windows: false

	// 文件扩展名处理

	filename := "config.json"
	// Ext 返回文件扩展名(包含点)
	ext := filepath.Ext(filename)
	fmt.Println("文件扩展名:", ext) // .json
	// 去除扩展名
	fmt.Println("去除扩展名:", strings.TrimSuffix(filename, ext)) // config

	// 计算相对路径

	// Rel 计算从 base 到 target 的相对路径
	rel, err := filepath.Rel("a/b", "a/b/t/file")
	if err != nil {
		log.Fatal("计算相对路径失败:", err)
	}
	fmt.Println("相对路径1:", rel) // t/file

	rel, err = filepath.Rel("a/b", "a/c/t/file")
	if err != nil {
		log.Fatal("计算相对路径失败:", err)
	}
	fmt.Println("相对路径2:", rel) // ../c/t/file

	// 获取绝对路径

	// Abs 返回路径的绝对表示形式
	absPath, err := filepath.Abs("relative/path")
	if err != nil {
		log.Fatal("获取绝对路径失败:", err)
	}
	fmt.Println("绝对路径:", absPath)

	// 路径分割

	// Split 将路径分割为目录和文件名部分
	dir, file := filepath.Split("/path/to/file.txt")
	fmt.Printf("分割结果 - 目录: %q, 文件名: %q\n", dir, file) // "/path/to/", "file.txt"

	// 路径分隔符

	// 获取操作系统特定的路径分隔符
	fmt.Println("路径分隔符:", string(filepath.Separator))

	// 路径列表操作

	// 将路径列表用操作系统特定的列表分隔符连接
	paths := []string{"path1", "path2", "path3"}
	fmt.Println("路径列表:", filepath.Join(paths...))

	// 路径清理

	// Clean通过纯词法处理返回等价的最短路径名
	fmt.Println("清理路径:", filepath.Clean("./path/../to/./file")) // to/file

	// 匹配文件名模式

	// Match 检查文件名是否匹配 shell 文件名模式
	matched, err := filepath.Match("*.go", "main.go")
	if err != nil {
		log.Fatal("模式匹配失败:", err)
	}
	fmt.Println("是否匹配:", matched) // true

	// 获取临时目录

	// TempDir 返回用于临时文件的默认目录
	fmt.Println("临时目录:", os.TempDir())

	// 获取用户配置目录

	// UserConfigDir 返回用户配置目录
	configDir, err := os.UserConfigDir()
	if err != nil {
		log.Fatal("获取用户配置目录失败:", err)
	}
	fmt.Println("用户配置目录:", configDir)

	// 获取用户主目录

	// UserHomeDir 返回当前用户的主目录
	homeDir, err := os.UserHomeDir()
	if err != nil {
		log.Fatal("获取用户主目录失败:", err)
	}
	fmt.Println("用户主目录:", homeDir)
}

实际应用场景

配置文件路径处理

go
func getConfigPath() string {
    return filepath.Join(os.Getenv("HOME"), ".config", "myapp.cfg")
}

Web 服务器静态文件服务

go
func safeJoin(base, userPath string) (string, error) {
    // 防止目录穿越攻击
    return filepath.Join(base, filepath.Clean("/"+userPath)), nil
}

日志文件轮转

go
func getNextLogPath(path string) string {
    ext := filepath.Ext(path)
    base := strings.TrimSuffix(path, ext)
    return fmt.Sprintf("%s_%d%s", base, time.Now().Unix(), ext)
}

遍历目录

filepath.Walk

功能: 递归遍历目录及其子目录,对每个文件/目录调用回调函数。

语法:

go
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
    // 处理逻辑
    return nil
})

特点:

  1. 基于 os.FileInfo
  1. 深度优先遍历
  1. 错误处理
  1. 适用场景

示例:

go
package main

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

func main() {
    err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        fmt.Println(path, info.Size())
        return nil
    })
    if err != nil {
        fmt.Println("遍历出错:", err)
    }
}

filepath.WalkDir

Go 1.16+ 推荐方式

功能 ​: 更高效的目录遍历方式,减少 os.Lstat 调用,提升性能。

语法:

go
// 使用 os.ReadDir(非递归)
entries, err := os.ReadDir(dirName)

// 使用 filepath.WalkDir(递归)
err := filepath.WalkDir(rootDir, func(path string, d os.DirEntry, err error) error {
    // 处理逻辑
    return nil
})

特点 ​:

  1. 基于 os.DirEntry
  1. 更细粒度控制
  1. 适用场景

示例:

go
package main

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

func main() {
    // 方式1: os.ReadDir(单层遍历)
    entries, err := os.ReadDir(".")
    if err != nil {
        panic(err)
    }
    for _, entry := range entries {
        fmt.Println(entry.Name(), entry.IsDir())
    }

    // 方式2: filepath.WalkDir(递归遍历)
    err = filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
        if err != nil {
            return err
        }
        fmt.Println(path, d.IsDir())
        return nil
    })
}

关键区别总结

特性 filepath.Walk os.ReadDir/filepath.WalkDir
底层实现 每次调用 os.Lstat 延迟加载文件信息(减少 stat 调用)
性能 较慢(频繁系统调用) 更快(优化后的元数据访问)
返回类型 os.FileInfo os.DirEntry
适用场景 需要文件详细信息 仅需文件名或类型
Go 版本 所有版本 Go 1.16+(推荐)

技巧

快速获取文件名列表

go
entries, _ := os.ReadDir(".")
for _, entry := range entries {
    fmt.Println(entry.Name())
}

跳过子目录(WalkDir 专属)​

go
err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
    if d.IsDir() && d.Name() == "skipme" {
        return fs.SkipDir // 跳过该目录
    }
    fmt.Println(path)
    return nil
})

仅统计文件数量

go
count := 0
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
    if !d.IsDir() {
        count++
    }
    return nil
})
fmt.Println("文件总数:", count)

临时文件和目录

是开发需要处理临时文件/目录的工具或服务的必备模式。

go
package main

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

// 错误检查辅助函数
func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// 1. 创建临时文件
	f, err := os.CreateTemp("", "sample") // 参数1: 目录(空表示默认临时目录),参数2: 文件名前缀
	check(err)
	fmt.Println("Temp file name:", f.Name()) // 输出完整路径如 /tmp/sample123456

	// 确保程序退出时删除临时文件
	defer os.Remove(f.Name())

	// 2. 写入二进制数据
	_, err = f.Write([]byte{1, 2, 3, 4})
	check(err)

	// 3. 创建临时目录
	dname, err := os.MkdirTemp("", "sampledir") // 同样可指定目录和前缀
	check(err)
	fmt.Println("Temp dir name:", dname) // 输出如 /tmp/sampledir789012

	// 确保程序退出时删除整个临时目录
	defer os.RemoveAll(dname)

	// 4. 在临时目录中创建文件
	fname := filepath.Join(dname, "file1")
	err = os.WriteFile(fname, []byte{1, 2}, 0666) // 权限-rw-rw-rw-
	check(err)
}

实际应用场景

下载临时存储

go
tempFile, _ := os.CreateTemp("", "download-")
defer os.Remove(tempFile.Name())
io.Copy(tempFile, resp.Body) // 从网络下载到临时文件

数据处理中间文件

go
tempDir, _ := os.MkdirTemp("", "process-")
defer os.RemoveAll(tempDir)
// 在tempDir中创建多个中间文件进行处理

单元测试临时空间

go
func TestSomething(t *testing.T) {
    tmp := t.TempDir() // 测试专用临时目录(自动清理)
    // 创建测试文件...
}

简单案例

文件操作

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 写入文件
	data := []byte("WangLei so cool!")

	// Deprecated: As of Go 1.16, this function simply calls [os.WriteFile].
	//err := ioutil.WriteFile("test.txt", data, 0644)

	err := os.WriteFile("test.txt", data, 0644)
	if err != nil {
		fmt.Println("写入文件错误:", err)
		return
	}
	fmt.Println("文件写入成功")

	// 读取文件
	// Deprecated: As of Go 1.16, this function simply calls [os.ReadFile].
	//content, err := ioutil.ReadFile("test.txt")

	content, err := os.ReadFile("test.txt")
	if err != nil {
		fmt.Println("读取文件错误:", err)
		return
	}
	fmt.Println("文件内容:", string(content))

	// 检查文件是否存在
	if _, err := os.Stat("test.txt"); os.IsNotExist(err) {
		fmt.Println("文件不存在")
	} else {
		fmt.Println("文件存在")
	}

	// 删除文件
	err = os.Remove("test.txt")
	if err != nil {
		fmt.Println("删除文件错误:", err)
		return
	}
	fmt.Println("文件删除成功")
}

目录操作

go
package main

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

// 错误检查辅助函数
func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// 1. 创建单个目录
	err := os.Mkdir("subdir", 0755) // 755权限:rwxr-xr-x
	check(err)

	// 确保最终删除整个目录
	defer os.RemoveAll("subdir")

	// 2. 创建空文件的辅助函数
	createEmptyFile := func(name string) {
		d := []byte("")
		check(os.WriteFile(name, d, 0644)) // 644权限:rw-r--r--
	}

	// 3. 创建目录结构
	createEmptyFile("subdir/file1")
	err = os.MkdirAll("subdir/parent/child", 0755) // 递归创建目录
	check(err)
	createEmptyFile("subdir/parent/file2")
	createEmptyFile("subdir/parent/file3")
	createEmptyFile("subdir/parent/child/file4")

	// 4. 读取目录内容
	c, err := os.ReadDir("subdir/parent")
	check(err)
	fmt.Println("Listing subdir/parent")
	for _, entry := range c {
		fmt.Println(" ", entry.Name(), entry.IsDir()) // 输出文件名和是否是目录
	}

	// 5. 切换工作目录
	err = os.Chdir("subdir/parent/child")
	check(err)
	c, err = os.ReadDir(".") // 读取当前目录
	check(err)
	fmt.Println("Listing subdir/parent/child")
	for _, entry := range c {
		fmt.Println(" ", entry.Name(), entry.IsDir())
	}

	// 6. 返回原始目录
	err = os.Chdir("../../..")
	check(err)

	// 7. 递归遍历目录
	fmt.Println("Visiting subdir")
	err = filepath.WalkDir("subdir", visit) // 使用自定义visit函数
	check(err)
}

// 目录遍历回调函数
func visit(path string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}
	fmt.Println(" ", path, d.IsDir())
	return nil
}

文件复制

go
package main

import (
	"fmt"
	"io"
	"os"
)

func copyFile(src, dst string) (int64, error) {
	source, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer source.Close()

	destination, err := os.Create(dst)
	if err != nil {
		return 0, err
	}
	defer destination.Close()

	nBytes, err := io.Copy(destination, source)
	return nBytes, err
}

func main() {
	if len(os.Args) != 3 {
		fmt.Println("用法: filecopy <源文件> <目标文件>")
		return
	}

	src := os.Args[1]
	dst := os.Args[2]

	bytesCopied, err := copyFile(src, dst)
	if err != nil {
		fmt.Println("复制文件错误:", err)
		return
	}

	fmt.Printf("成功复制 %d 字节从 %s 到 %s\n", bytesCopied, src, dst)
}

CSV 文件读写

go
package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"strconv"
)

type Employee struct {
	Name   string
	Age    int
	Salary float64
}

func readCSV(filename string) ([]Employee, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		return nil, err
	}

	var employees []Employee

	for i, record := range records {
		if i == 0 {
			continue // 跳过标题行
		}

		age, err := strconv.Atoi(record[1])
		if err != nil {
			return nil, err
		}

		salary, err := strconv.ParseFloat(record[2], 64)
		if err != nil {
			return nil, err
		}

		employees = append(employees, Employee{
			Name:   record[0],
			Age:    age,
			Salary: salary,
		})
	}

	return employees, nil
}

func writeCSV(filename string, employees []Employee) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	// 写入标题行
	if err := writer.Write([]string{"Name", "Age", "Salary"}); err != nil {
		return err
	}

	// 写入数据
	for _, emp := range employees {
		record := []string{
			emp.Name,
			strconv.Itoa(emp.Age),
			strconv.FormatFloat(emp.Salary, 'f', 2, 64),
		}
		if err := writer.Write(record); err != nil {
			return err
		}
	}

	return nil
}

func filterEmployees(employees []Employee, minSalary float64) []Employee {
	var result []Employee
	for _, emp := range employees {
		if emp.Salary >= minSalary {
			result = append(result, emp)
		}
	}
	return result
}

func main() {
	// 读取CSV文件
	employees, err := readCSV("employees.csv")
	if err != nil {
		fmt.Printf("读取CSV错误: %v\n", err)
		return
	}

	fmt.Println("所有员工:")
	for _, emp := range employees {
		fmt.Printf("%s, %d岁, 薪资: %.2f\n", emp.Name, emp.Age, emp.Salary)
	}

	// 过滤高薪员工
	highPaid := filterEmployees(employees, 50000)
	fmt.Println("\n高薪员工(>=50000):")
	for _, emp := range highPaid {
		fmt.Printf("%s, %d岁, 薪资: %.2f\n", emp.Name, emp.Age, emp.Salary)
	}

	// 写入新的CSV文件
	if err := writeCSV("high_paid_employees.csv", highPaid); err != nil {
		fmt.Printf("写入CSV错误: %v\n", err)
		return
	}

	fmt.Println("\n高薪员工已保存到: high_paid_employees.csv")
}

文件加密与解密工具

go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"os"
)

func encryptFile(inputFile, outputFile string, key []byte) error {
	// 读取原始文件
	plaintext, err := os.ReadFile(inputFile)
	if err != nil {
		return err
	}

	// 创建加密块
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	// 创建GCM模式的加密器
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}

	// 创建随机nonce
	nonce := make([]byte, gcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		return err
	}

	// 加密数据
	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)

	// 写入加密文件
	return os.WriteFile(outputFile, ciphertext, 0644)
}

func decryptFile(inputFile, outputFile string, key []byte) error {
	// 读取加密文件
	ciphertext, err := os.ReadFile(inputFile)
	if err != nil {
		return err
	}

	// 创建加密块
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	// 创建GCM模式的解密器
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}

	// 提取nonce
	nonceSize := gcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return errors.New("加密文件已损坏")
	}

	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

	// 解密数据
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return err
	}

	// 写入解密文件
	return os.WriteFile(outputFile, plaintext, 0644)
}

func generateKey() ([]byte, error) {
	key := make([]byte, 32) // AES-256
	if _, err := rand.Read(key); err != nil {
		return nil, err
	}
	return key, nil
}

func main() {
	// 生成加密密钥
	key, err := generateKey()
	if err != nil {
		fmt.Printf("生成密钥错误: %v\n", err)
		return
	}

	// 保存密钥到文件(实际应用中应该安全存储)
	if err := os.WriteFile("secret.key", key, 0644); err != nil {
		fmt.Printf("保存密钥错误: %v\n", err)
		return
	}

	inputFile := "test.txt"
	encryptedFile := "test.enc"
	decryptedFile := "test.dec"

	// 创建测试文件
	if err := os.WriteFile(inputFile, []byte("这是一个需要加密的秘密文件"), 0644); err != nil {
		fmt.Printf("创建测试文件错误: %v\n", err)
		return
	}

	// 加密文件
	fmt.Println("正在加密文件...")
	if err := encryptFile(inputFile, encryptedFile, key); err != nil {
		fmt.Printf("加密错误: %v\n", err)
		return
	}

	// 解密文件
	fmt.Println("正在解密文件...")
	if err := decryptFile(encryptedFile, decryptedFile, key); err != nil {
		fmt.Printf("解密错误: %v\n", err)
		return
	}

	fmt.Println("操作完成!")
	fmt.Println("原始文件:", inputFile)
	fmt.Println("加密文件:", encryptedFile)
	fmt.Println("解密文件:", decryptedFile)
	fmt.Println("加密密钥已保存到: secret.key")
}

配置文件管理工具

go
package main

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

type Config struct {
	Server struct {
		Host string `json:"host"`
		Port int    `json:"port"`
	} `json:"server"`
	Database struct {
		Username string `json:"username"`
		Password string `json:"password"`
		Name     string `json:"name"`
	} `json:"database"`
	Logging struct {
		Level    string `json:"level"`
		FilePath string `json:"file_path"`
	} `json:"logging"`
}

func loadConfig(configPath string) (*Config, error) {
	// 获取绝对路径
	absPath, err := filepath.Abs(configPath)
	if err != nil {
		return nil, err
	}

	// 读取文件
	data, err := os.ReadFile(absPath)
	if err != nil {
		return nil, err
	}

	// 解析JSON
	var config Config
	if err := json.Unmarshal(data, &config); err != nil {
		return nil, err
	}

	return &config, nil
}

func saveConfig(configPath string, config *Config) error {
	// 序列化为JSON
	data, err := json.MarshalIndent(config, "", "  ")
	if err != nil {
		return err
	}

	// 写入文件
	return os.WriteFile(configPath, data, 0644)
}

func createDefaultConfig() *Config {
	var config Config

	// 设置默认服务器配置
	config.Server.Host = "localhost"
	config.Server.Port = 8080

	// 设置默认数据库配置
	config.Database.Username = "admin"
	config.Database.Password = "password"
	config.Database.Name = "appdb"

	// 设置默认日志配置
	config.Logging.Level = "info"
	config.Logging.FilePath = "./app.log"

	return &config
}

func main() {
	configFile := "config.json"

	// 检查配置文件是否存在
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		fmt.Println("配置文件不存在,创建默认配置...")
		defaultConfig := createDefaultConfig()
		if err := saveConfig(configFile, defaultConfig); err != nil {
			fmt.Printf("创建配置文件错误: %v\n", err)
			return
		}
		fmt.Println("默认配置文件已创建:", configFile)
	}

	// 加载配置文件
	config, err := loadConfig(configFile)
	if err != nil {
		fmt.Printf("加载配置文件错误: %v\n", err)
		return
	}

	// 打印配置信息
	fmt.Println("当前配置:")
	fmt.Printf("服务器: %s:%d\n", config.Server.Host, config.Server.Port)
	fmt.Printf("数据库: %s@%s/%s\n", config.Database.Username, "******", config.Database.Name)
	fmt.Printf("日志级别: %s, 路径: %s\n", config.Logging.Level, config.Logging.FilePath)

	// 修改配置示例
	fmt.Println("\n修改服务器端口为 9090...")
	config.Server.Port = 9090
	if err := saveConfig(configFile, config); err != nil {
		fmt.Printf("保存配置错误: %v\n", err)
		return
	}
	fmt.Println("配置已更新!")
}

文件系统使用统计工具

go
package main

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

type FileStats struct {
	TotalFiles int
	TotalSize  int64
	ByExt      map[string]struct {
		Count int
		Size  int64
	}
	mu sync.Mutex
}

func NewFileStats() *FileStats {
	return &FileStats{
		ByExt: make(map[string]struct {
			Count int
			Size  int64
		}),
	}
}

func (fs *FileStats) AddFile(path string, info os.FileInfo) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	ext := filepath.Ext(path)
	if ext == "" {
		ext = "无扩展名"
	}

	entry := fs.ByExt[ext]
	entry.Count++
	entry.Size += info.Size()
	fs.ByExt[ext] = entry

	fs.TotalFiles++
	fs.TotalSize += info.Size()
}

func (fs *FileStats) Print() {
	fmt.Printf("总文件数: %d\n", fs.TotalFiles)
	fmt.Printf("总大小: %.2f MB\n", float64(fs.TotalSize)/(1024*1024))
	fmt.Println("\n按扩展名统计:")
	for ext, stat := range fs.ByExt {
		fmt.Printf("%s: %d 个文件, %.2f MB\n",
			ext,
			stat.Count,
			float64(stat.Size)/(1024*1024))
	}
}

func walkDir(dir string, fileStats *FileStats, wg *sync.WaitGroup) {
	defer wg.Done()

	entries, err := os.ReadDir(dir)
	if err != nil {
		return
	}

	for _, entry := range entries {
		fullPath := filepath.Join(dir, entry.Name())
		if entry.IsDir() {
			wg.Add(1)
			go walkDir(fullPath, fileStats, wg)
		} else {
			info, err := entry.Info()
			if err == nil {
				fileStats.AddFile(fullPath, info)
			}
		}
	}
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("用法: filestats <目录>")
		return
	}

	rootDir := os.Args[1]
	startTime := time.Now()

	fileStats := NewFileStats()
	var wg sync.WaitGroup

	wg.Add(1)
	go walkDir(rootDir, fileStats, &wg)
	wg.Wait()

	fmt.Printf("\n扫描完成, 耗时: %.2f 秒\n", time.Since(startTime).Seconds())
	fileStats.Print()
}

文件内容替换工具

go
package main

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

type ReplaceTask struct {
	FilePath string
	OldText  string
	NewText  string
}

func replaceInFile(task ReplaceTask) error {
	// 读取文件内容
	content, err := os.ReadFile(task.FilePath)
	if err != nil {
		return err
	}

	// 替换内容
	newContent := strings.ReplaceAll(string(content), task.OldText, task.NewText)

	// 如果内容没有变化,直接返回
	if string(content) == newContent {
		return nil
	}

	// 创建备份文件
	backupPath := task.FilePath + ".bak"
	if err := os.WriteFile(backupPath, content, 0644); err != nil {
		return err
	}

	// 写入新内容
	if err := os.WriteFile(task.FilePath, []byte(newContent), 0644); err != nil {
		// 如果出错,尝试恢复备份
		os.WriteFile(task.FilePath, content, 0644)
		os.Remove(backupPath)
		return err
	}

	return nil
}

func processFiles(rootDir, oldText, newText string) error {
	var wg sync.WaitGroup
	taskChan := make(chan ReplaceTask, 100)
	errorChan := make(chan error, 10)

	// 启动工作池
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for task := range taskChan {
				if err := replaceInFile(task); err != nil {
					errorChan <- fmt.Errorf("%s: %v", task.FilePath, err)
				}
			}
		}()
	}

	// 遍历目录并发送任务
	go func() {
		err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			// 跳过所有以点号开头的文件和目录
			if strings.HasPrefix(info.Name(), ".") {
				if info.IsDir() {
					return filepath.SkipDir // 如果是目录,跳过整个目录
				}
				return nil // 如果是文件,跳过这个文件
			}

			if !info.IsDir() && !strings.HasSuffix(path, ".bak") {
				taskChan <- ReplaceTask{
					FilePath: path,
					OldText:  oldText,
					NewText:  newText,
				}
			}
			return nil
		})
		if err != nil {
			errorChan <- err
		}
		close(taskChan)
	}()

	// 等待所有工作完成
	go func() {
		wg.Wait()
		close(errorChan)
	}()

	// 收集错误
	var errors []string
	for err := range errorChan {
		errors = append(errors, err.Error())
	}

	if len(errors) > 0 {
		return fmt.Errorf("遇到 %d 个错误:\n%s", len(errors), strings.Join(errors, "\n"))
	}

	return nil
}

func main() {
	if len(os.Args) != 4 {
		fmt.Println("用法: filereplace <目录> <旧文本> <新文本>")
		fmt.Println("示例: filereplace ./src \"旧版本\" \"新版本\"")
		return
	}

	rootDir := os.Args[1]
	oldText := os.Args[2]
	newText := os.Args[3]

	fmt.Printf("将在目录 %s 中替换 \"%s\" 为 \"%s\"\n", rootDir, oldText, newText)
	fmt.Print("确定要继续吗? (y/n): ")

	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	if strings.ToLower(scanner.Text()) != "y" {
		fmt.Println("操作已取消")
		return
	}

	if err := processFiles(rootDir, oldText, newText); err != nil {
		fmt.Printf("替换过程中出错: %v\n", err)
	} else {
		fmt.Println("替换完成!")
	}
}

文件同步工具(单向同步)

go
package main

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

type SyncFile struct {
	Path    string
	Size    int64
	ModTime time.Time
}

func collectFiles(dir string) (map[string]SyncFile, error) {
	files := make(map[string]SyncFile)

	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			relPath, err := filepath.Rel(dir, path)
			if err != nil {
				return err
			}

			files[relPath] = SyncFile{
				Path:    path,
				Size:    info.Size(),
				ModTime: info.ModTime(),
			}
		}

		return nil
	})

	return files, err
}

func syncFiles(sourceDir, targetDir string, dryRun bool) error {
	// 收集源文件和目标文件
	sourceFiles, err := collectFiles(sourceDir)
	if err != nil {
		return fmt.Errorf("收集源文件错误: %v", err)
	}

	targetFiles, err := collectFiles(targetDir)
	if err != nil {
		return fmt.Errorf("收集目标文件错误: %v", err)
	}

	// 创建目标目录中不存在的子目录
	for relPath := range sourceFiles {
		dir := filepath.Dir(relPath)
		if dir != "." {
			targetSubDir := filepath.Join(targetDir, dir)
			if _, err := os.Stat(targetSubDir); os.IsNotExist(err) {
				if !dryRun {
					if err := os.MkdirAll(targetSubDir, 0755); err != nil {
						return fmt.Errorf("创建目录错误: %v", err)
					}
				}
				fmt.Printf("创建目录: %s\n", targetSubDir)
			}
		}
	}

	// 比较并同步文件
	for relPath, sourceFile := range sourceFiles {
		targetFile, exists := targetFiles[relPath]
		targetPath := filepath.Join(targetDir, relPath)

		if !exists {
			// 文件在目标目录中不存在,需要复制
			fmt.Printf("新增文件: %s\n", relPath)
			if !dryRun {
				if err := copyFile(sourceFile.Path, targetPath); err != nil {
					return fmt.Errorf("复制文件错误: %v", err)
				}
			}
		} else {
			// 文件已存在,检查是否需要更新
			if sourceFile.ModTime.After(targetFile.ModTime) || sourceFile.Size != targetFile.Size {
				fmt.Printf("更新文件: %s (源: %s, 目标: %s)\n",
					relPath,
					sourceFile.ModTime.Format("2006-01-02 15:04:05"),
					targetFile.ModTime.Format("2006-01-02 15:04:05"))
				if !dryRun {
					if err := copyFile(sourceFile.Path, targetPath); err != nil {
						return fmt.Errorf("复制文件错误: %v", err)
					}
				}
			}
		}
	}

	// 检查目标目录中有而源目录中没有的文件
	for relPath := range targetFiles {
		if _, exists := sourceFiles[relPath]; !exists {
			targetPath := filepath.Join(targetDir, relPath)
			fmt.Printf("删除文件: %s\n", relPath)
			if !dryRun {
				if err := os.Remove(targetPath); err != nil {
					return fmt.Errorf("删除文件错误: %v", err)
				}
			}
		}
	}

	return nil
}

func copyFile(src, dst string) error {
	source, err := os.Open(src)
	if err != nil {
		return err
	}
	defer source.Close()

	destination, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer destination.Close()

	_, err = io.Copy(destination, source)
	if err != nil {
		return err
	}

	// 同步文件权限和修改时间
	sourceInfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	if err := os.Chmod(dst, sourceInfo.Mode()); err != nil {
		return err
	}

	if err := os.Chtimes(dst, sourceInfo.ModTime(), sourceInfo.ModTime()); err != nil {
		return err
	}

	return nil
}

func ensureDir(dirPath string) error {
	// MkdirAll 会创建所有不存在的父目录
	err := os.MkdirAll(dirPath, 0755)
	if err != nil {
		return fmt.Errorf("创建目录失败: %v", err)
	}
	return nil
}

func main() {
	if len(os.Args) < 3 {
		fmt.Println("用法: filesync <源目录> <目标目录> [--dry-run]")
		return
	}

	sourceDir := os.Args[1]
	targetDir := os.Args[2]
	dryRun := len(os.Args) > 3 && os.Args[3] == "--dry-run"

	if dryRun {
		fmt.Println("!!! 干运行模式,不会实际修改文件 !!!")
	}

	// 如果目标目录不存在 则创建
	if err := ensureDir(targetDir); err != nil {
		fmt.Println("目标目录创建失败")
		return
	}

	fmt.Printf("同步 %s -> %s\n", sourceDir, targetDir)
	if err := syncFiles(sourceDir, targetDir, dryRun); err != nil {
		fmt.Printf("同步错误: %v\n", err)
	} else {
		fmt.Println("同步完成!")
	}
}

文件监控

go
package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/fsnotify/fsnotify"
)

func watchDir(path string) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("创建监控器失败: %v", err)
	}
	defer watcher.Close()

	// 使用 context 或信号来控制退出
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	// 递归添加监控
	err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			if err := watcher.Add(path); err != nil {
				return fmt.Errorf("添加监控失败: %s: %v", path, err)
			}
			log.Printf("正在监控目录: %s", path)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("遍历目录失败: %v", err)
	}

	// 事件处理循环
	for {
		select {
		case event, ok := <-watcher.Events:
			if !ok {
				return nil // 通道关闭
			}
			switch {
			case event.Op&fsnotify.Write == fsnotify.Write:
				log.Printf("文件修改: %s", event.Name)
			case event.Op&fsnotify.Create == fsnotify.Create:
				log.Printf("文件创建: %s", event.Name)
				// 如果是新目录,自动添加监控
				if info, err := os.Stat(event.Name); err == nil && info.IsDir() {
					if err := watcher.Add(event.Name); err != nil {
						log.Printf("添加新目录监控失败: %s: %v", event.Name, err)
					}
				}
			case event.Op&fsnotify.Remove == fsnotify.Remove:
				log.Printf("文件删除: %s", event.Name)
			case event.Op&fsnotify.Rename == fsnotify.Rename:
				log.Printf("文件重命名: %s", event.Name)
			}

		case err, ok := <-watcher.Errors:
			if !ok {
				return nil
			}
			log.Printf("监控错误: %v", err)

		case <-quit:
			log.Println("收到退出信号,停止监控")
			return nil
		}
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("用法: filewatch <目录路径>")
		os.Exit(1)
	}

	path := os.Args[1]
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatalf("目录不存在: %s", path)
	}

	log.Printf("开始监控目录: %s", path)
	if err := watchDir(path); err != nil {
		log.Fatal(err)
	}
}

文件系统监控与自动备份

go
package main

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"io"
	"log"
	"os"
	"path/filepath"
)

type BackupManager struct {
	SourceDir string
	BackupDir string
	Watcher   *fsnotify.Watcher
}

func NewBackupManager(source, backup string) (*BackupManager, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	// 确保备份目录存在
	if err := os.MkdirAll(backup, 0755); err != nil {
		return nil, err
	}

	return &BackupManager{
		SourceDir: source,
		BackupDir: backup,
		Watcher:   watcher,
	}, nil
}

func (bm *BackupManager) Start() error {
	// 初始完整备份
	if err := bm.fullBackup(); err != nil {
		return err
	}

	// 启动监控
	if err := bm.Watcher.Add(bm.SourceDir); err != nil {
		return err
	}

	go bm.watchChanges()

	return nil
}

func (bm *BackupManager) fullBackup() error {
	return filepath.Walk(bm.SourceDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		relPath, err := filepath.Rel(bm.SourceDir, path)
		if err != nil {
			return err
		}

		backupPath := filepath.Join(bm.BackupDir, relPath)

		if info.IsDir() {
			return os.MkdirAll(backupPath, info.Mode())
		}

		return bm.backupFile(path, backupPath)
	})
}

func (bm *BackupManager) backupFile(source, dest string) error {
	srcFile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
		return err
	}

	destFile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destFile.Close()

	_, err = io.Copy(destFile, srcFile)
	return err
}

func (bm *BackupManager) watchChanges() {
	for {
		select {
		case event, ok := <-bm.Watcher.Events:
			if !ok {
				return
			}

			if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
				relPath, err := filepath.Rel(bm.SourceDir, event.Name)
				if err != nil {
					log.Printf("获取相对路径错误: %v", err)
					continue
				}

				backupPath := filepath.Join(bm.BackupDir, relPath)
				if err := bm.backupFile(event.Name, backupPath); err != nil {
					log.Printf("备份文件 %s 错误: %v", event.Name, err)
				} else {
					log.Printf("已备份: %s -> %s", event.Name, backupPath)
				}
			}

		case err, ok := <-bm.Watcher.Errors:
			if !ok {
				return
			}
			log.Printf("监控错误: %v", err)
		}
	}
}

func (bm *BackupManager) Stop() {
	bm.Watcher.Close()
}

func main() {
	if len(os.Args) != 3 {
		fmt.Println("用法: autobackup <源目录> <备份目录>")
		return
	}

	sourceDir := os.Args[1]
	backupDir := os.Args[2]

	backupManager, err := NewBackupManager(sourceDir, backupDir)
	if err != nil {
		log.Fatalf("创建备份管理器失败: %v", err)
	}

	fmt.Printf("启动自动备份服务,源目录: %s, 备份目录: %s\n", sourceDir, backupDir)
	fmt.Println("按Ctrl+C停止服务...")

	if err := backupManager.Start(); err != nil {
		log.Fatalf("启动备份失败: %v", err)
	}

	// 保持程序运行
	select {}
}

压缩与解压缩

go
package main

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

func compressFiles(zipName string, paths []string) error {
	zipFile, err := os.Create(zipName)
	if err != nil {
		return err
	}
	defer zipFile.Close()

	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	for _, path := range paths {
		err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			// 创建文件头信息
			header, err := zip.FileInfoHeader(info)
			if err != nil {
				return err
			}

			// 设置压缩方法
			header.Method = zip.Deflate

			// 调整路径,去掉最前面的路径部分
			relPath, err := filepath.Rel(filepath.Dir(path), filePath)
			if err != nil {
				return err
			}
			header.Name = relPath

			// 如果是目录,只需要创建目录条目
			if info.IsDir() {
				header.Name += "/" // 确保目录名以/结尾
				_, err = zipWriter.CreateHeader(header)
				return err
			}

			// 普通文件
			writer, err := zipWriter.CreateHeader(header)
			if err != nil {
				return err
			}

			// 打开并写入文件内容
			file, err := os.Open(filePath)
			if err != nil {
				return err
			}
			defer file.Close()

			_, err = io.Copy(writer, file)
			return err
		})

		if err != nil {
			return err
		}
	}

	return nil
}

func decompressZip(zipName, destDir string) error {
	reader, err := zip.OpenReader(zipName)
	if err != nil {
		return err
	}
	defer reader.Close()

	for _, file := range reader.File {
		path := filepath.Join(destDir, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
			return err
		}

		outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer outFile.Close()

		zipFile, err := file.Open()
		if err != nil {
			return err
		}
		defer zipFile.Close()

		if _, err := io.Copy(outFile, zipFile); err != nil {
			return err
		}
	}

	return nil
}

func main() {
	// 压缩示例
	filesToCompress := []string{"file1.md", "file2.md", "gorm"}
	err := compressFiles("archive.zip", filesToCompress)
	if err != nil {
		fmt.Println("压缩失败:", err)
	} else {
		fmt.Println("文件压缩成功")
	}

	// 解压示例
	err = decompressZip("archive.zip", "extracted")
	if err != nil {
		fmt.Println("解压失败:", err)
	} else {
		fmt.Println("文件解压成功")
	}
}

文件差异比较工具

go
package main

import (
	"bufio"
	"fmt"
	"os"
)

// FileDiff 表示文件差异的结构体
type FileDiff struct {
	LineNum int    // 行号
	Left    string // 第一个文件的内容
	Right   string // 第二个文件的内容
}

// compareFiles 比较两个文件的内容差异
func compareFiles(file1, file2 string) ([]FileDiff, error) {
	// 同时打开两个文件
	f1, err := os.Open(file1)
	if err != nil {
		return nil, fmt.Errorf("打开文件 %s 失败: %w", file1, err)
	}
	defer f1.Close()

	f2, err := os.Open(file2)
	if err != nil {
		return nil, fmt.Errorf("打开文件 %s 失败: %w", file2, err)
	}
	defer f2.Close()

	var diffs []FileDiff
	scanner1 := bufio.NewScanner(f1)
	scanner2 := bufio.NewScanner(f2)

	lineNum := 1
	// 使用更简洁的循环控制
	for {
		hasLine1 := scanner1.Scan()
		hasLine2 := scanner2.Scan()

		// 两个文件都读取完毕时退出循环
		if !hasLine1 && !hasLine2 {
			break
		}

		text1 := ""
		if hasLine1 {
			text1 = scanner1.Text()
		}

		text2 := ""
		if hasLine2 {
			text2 = scanner2.Text()
		}

		if text1 != text2 {
			diffs = append(diffs, FileDiff{
				LineNum: lineNum,
				Left:    text1,
				Right:   text2,
			})
		}
		lineNum++
	}

	// 检查扫描错误
	if err := scanner1.Err(); err != nil {
		return nil, fmt.Errorf("读取文件 %s 失败: %w", file1, err)
	}

	if err := scanner2.Err(); err != nil {
		return nil, fmt.Errorf("读取文件 %s 失败: %w", file2, err)
	}

	return diffs, nil
}

// printDiff 打印文件差异
func printDiff(diffs []FileDiff) {
	if len(diffs) == 0 {
		fmt.Println("文件内容相同")
		return
	}

	fmt.Printf("发现 %d 处差异:\n", len(diffs))
	for _, diff := range diffs {
		fmt.Printf("行号 %d:\n", diff.LineNum)
		if diff.Left != "" {
			fmt.Printf("< %s\n", diff.Left)
		} else {
			fmt.Println("< (空行或文件结束)")
		}
		if diff.Right != "" {
			fmt.Printf("> %s\n", diff.Right)
		} else {
			fmt.Println("> (空行或文件结束)")
		}
		fmt.Println("---")
	}
}

func main() {
	if len(os.Args) != 3 {
		fmt.Printf("用法: %s <文件1> <文件2>\n", os.Args[0])
		os.Exit(1)
	}

	file1 := os.Args[1]
	file2 := os.Args[2]

	diffs, err := compareFiles(file1, file2)
	if err != nil {
		fmt.Printf("错误: %v\n", err)
		os.Exit(1)
	}

	printDiff(diffs)
}

文件搜索工具

go
package main

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

type SearchResult struct {
	Path  string
	Error error
}

func searchInFile(path string, searchTerm string, results chan<- SearchResult, wg *sync.WaitGroup) {
	defer wg.Done()

	content, err := os.ReadFile(path)
	if err != nil {
		results <- SearchResult{Path: path, Error: err}
		return
	}

	if strings.Contains(string(content), searchTerm) {
		results <- SearchResult{Path: path, Error: nil}
	}
}

func fileSearch(rootDir string, searchTerm string) {
	var wg sync.WaitGroup
	results := make(chan SearchResult, 100)

	// 遍历目录并启动搜索goroutine
	err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && !strings.HasPrefix(filepath.Base(path), ".") {
			wg.Add(1)
			go searchInFile(path, searchTerm, results, &wg)
		}
		return nil
	})

	if err != nil {
		fmt.Printf("遍历目录错误: %v\n", err)
		return
	}

	// 等待所有goroutine完成
	go func() {
		wg.Wait()
		close(results)
	}()

	// 打印结果
	fmt.Printf("在 '%s' 中搜索 '%s' 的结果:\n", rootDir, searchTerm)
	found := false
	for result := range results {
		if result.Error != nil {
			fmt.Printf("%s: 搜索错误: %v\n", result.Path, result.Error)
		} else {
			fmt.Println(result.Path)
			found = true
		}
	}

	if !found {
		fmt.Println("未找到匹配的文件")
	}
}

func main() {
	if len(os.Args) != 3 {
		fmt.Println("用法: filesearch <目录> <搜索词>")
		return
	}

	rootDir := os.Args[1]
	searchTerm := os.Args[2]

	fileSearch(rootDir, searchTerm)
}

大文件分割与合并

go
package main

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

const chunkSize = 5 * 1024 * 1024 // 5MB

func splitFile(sourceFile string) error {
	file, err := os.Open(sourceFile)
	if err != nil {
		return err
	}
	defer file.Close()

	fileInfo, err := file.Stat()
	if err != nil {
		return err
	}

	fileSize := fileInfo.Size()
	totalParts := int(fileSize/chunkSize) + 1

	buffer := make([]byte, chunkSize)

	for i := 0; i < totalParts; i++ {
		partFileName := fmt.Sprintf("%s.part%d", sourceFile, i)
		partFile, err := os.Create(partFileName)
		if err != nil {
			return err
		}

		bytesRead, err := file.Read(buffer)
		if err != nil && err != io.EOF {
			partFile.Close()
			return err
		}

		_, err = partFile.Write(buffer[:bytesRead])
		partFile.Close()
		if err != nil {
			return err
		}

		fmt.Printf("分割完成: %s (%.2f%%)\n", partFileName, float64(i+1)*100/float64(totalParts))
	}

	return nil
}

func mergeFiles(originalName string) error {
	dir := filepath.Dir(originalName)
	base := filepath.Base(originalName)

	destFile, err := os.Create(originalName)
	if err != nil {
		return err
	}
	defer destFile.Close()

	i := 0
	for {
		partFileName := filepath.Join(dir, fmt.Sprintf("%s.part%d", base, i))
		partFile, err := os.Open(partFileName)
		if err != nil {
			if os.IsNotExist(err) {
				break
			}
			return err
		}

		_, err = io.Copy(destFile, partFile)
		partFile.Close()
		if err != nil {
			return err
		}

		fmt.Printf("合并完成: %s\n", partFileName)
		i++
	}

	return nil
}

func cleanupParts(originalName string) error {
	dir := filepath.Dir(originalName)
	base := filepath.Base(originalName)

	i := 0
	for {
		partFileName := filepath.Join(dir, fmt.Sprintf("%s.part%d", base, i))
		if _, err := os.Stat(partFileName); os.IsNotExist(err) {
			break
		}

		if err := os.Remove(partFileName); err != nil {
			return err
		}
		i++
	}

	return nil
}

func main() {
	if len(os.Args) < 3 {
		fmt.Println("用法:")
		fmt.Println("分割文件: fileparts split <文件名>")
		fmt.Println("合并文件: fileparts merge <文件名>")
		fmt.Println("清理碎片: fileparts cleanup <文件名>")
		return
	}

	command := os.Args[1]
	filename := os.Args[2]

	switch command {
	case "split":
		fmt.Printf("开始分割文件: %s\n", filename)
		if err := splitFile(filename); err != nil {
			fmt.Printf("分割文件错误: %v\n", err)
		} else {
			fmt.Println("文件分割完成")
		}
	case "merge":
		fmt.Printf("开始合并文件: %s\n", filename)
		if err := mergeFiles(filename); err != nil {
			fmt.Printf("合并文件错误: %v\n", err)
		} else {
			fmt.Println("文件合并完成")
		}
	case "cleanup":
		fmt.Printf("开始清理碎片文件: %s\n", filename)
		if err := cleanupParts(filename); err != nil {
			fmt.Printf("清理碎片错误: %v\n", err)
		} else {
			fmt.Println("碎片清理完成")
		}
	default:
		fmt.Println("未知命令")
	}
}

日志文件轮转工具

go
package main

import (
	"fmt"
	"os"
	"time"
)

type LogRotator struct {
	FilePath    string
	MaxSize     int64 // 最大文件大小(字节)
	BackupCount int   // 保留的备份文件数量
}

// Rotate 执行日志轮转
func (lr *LogRotator) Rotate() error {
	// 检查文件是否存在及大小
	info, err := os.Stat(lr.FilePath)
	if os.IsNotExist(err) {
		return nil // 文件不存在,无需轮转
	}
	if err != nil {
		return fmt.Errorf("获取文件信息失败: %w", err)
	}

	// 检查文件大小是否达到轮转阈值
	if info.Size() < lr.MaxSize {
		return nil
	}

	// 1. 先删除最旧的备份文件(如果存在)
	if lr.BackupCount > 0 {
		oldestBackup := lr.backupFileName(lr.BackupCount)
		if _, err := os.Stat(oldestBackup); err == nil {
			if err := os.Remove(oldestBackup); err != nil {
				return fmt.Errorf("删除旧备份文件失败: %w", err)
			}
		}
	}

	// 2. 依次将备份文件向后移动一位
	for i := lr.BackupCount - 1; i >= 1; i-- {
		oldName := lr.backupFileName(i)
		newName := lr.backupFileName(i + 1)

		if _, err := os.Stat(oldName); err == nil {
			if err := os.Rename(oldName, newName); err != nil {
				return fmt.Errorf("重命名备份文件失败: %w", err)
			}
		}
	}

	// 3. 将当前日志文件重命名为第一个备份
	if err := os.Rename(lr.FilePath, lr.backupFileName(1)); err != nil {
		return fmt.Errorf("重命名当前日志文件失败: %w", err)
	}

	return nil
}

// backupFileName 生成备份文件名
// index: 备份序号,从1开始(1表示最新备份)
func (lr *LogRotator) backupFileName(index int) string {
	return fmt.Sprintf("%s.%d", lr.FilePath, index)
}

// Write 写入日志并处理轮转
func (lr *LogRotator) Write(p []byte) (n int, err error) {
	if err := lr.Rotate(); err != nil {
		return 0, fmt.Errorf("日志轮转失败: %w", err)
	}

	file, err := os.OpenFile(lr.FilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return 0, fmt.Errorf("打开日志文件失败: %w", err)
	}
	defer file.Close()

	return file.Write(p)
}

func main() {
	// 创建日志轮转器
	rotator := &LogRotator{
		FilePath:    "app.log",
		MaxSize:     1024, // 1KB 测试用
		BackupCount: 3,    // 保留3个备份文件
	}

	// 模拟日志写入
	for i := 0; i < 100; i++ {
		logEntry := fmt.Sprintf("%s - Log entry %d\n", time.Now().Format(time.RFC3339), i)
		if _, err := rotator.Write([]byte(logEntry)); err != nil {
			fmt.Printf("写入日志错误: %v\n", err)
		}
		time.Sleep(100 * time.Millisecond) // 添加延迟以便观察
	}

	fmt.Println("日志轮转完成,检查当前目录下的 app.log* 文件")
}

并发文件哈希计算

go
package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
)

func calculateFileHash(path string, wg *sync.WaitGroup, results chan<- string) {
	defer wg.Done()

	file, err := os.Open(path)
	if err != nil {
		results <- fmt.Sprintf("%s: 打开文件错误: %v", path, err)
		return
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		results <- fmt.Sprintf("%s: 计算哈希错误: %v", path, err)
		return
	}

	hashInBytes := hash.Sum(nil)
	results <- fmt.Sprintf("%s: %s", path, hex.EncodeToString(hashInBytes))
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("用法: filehash <目录路径>")
		return
	}

	root := os.Args[1]
	var wg sync.WaitGroup
	results := make(chan string, 10)

	// 遍历目录并启动 goroutine 计算文件哈希
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			wg.Add(1)
			go calculateFileHash(path, &wg, results)
		}
		return nil
	})

	if err != nil {
		fmt.Printf("遍历目录错误: %v\n", err)
		return
	}

	// 等待所有goroutine完成
	go func() {
		wg.Wait()
		close(results)
	}()

	// 打印结果
	for result := range results {
		fmt.Println(result)
	}
}

文件上传服务(模拟)

go
package main

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

const uploadDir = "./uploads"
const maxUploadSize = 10 * 1024 * 1024 // 10MB

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// 解析multipart表单
	r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
	if err := r.ParseMultipartForm(maxUploadSize); err != nil {
		http.Error(w, "文件太大", http.StatusBadRequest)
		return
	}

	// 获取上传的文件
	file, handler, err := r.FormFile("uploadFile")
	if err != nil {
		http.Error(w, "读取文件错误", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// 创建上传目录
	if err := os.MkdirAll(uploadDir, os.ModePerm); err != nil {
		http.Error(w, "服务器错误", http.StatusInternalServerError)
		return
	}

	// 创建目标文件
	dstPath := filepath.Join(uploadDir, handler.Filename)
	dst, err := os.Create(dstPath)
	if err != nil {
		http.Error(w, "服务器错误", http.StatusInternalServerError)
		return
	}
	defer dst.Close()

	// 复制文件内容
	if _, err := io.Copy(dst, file); err != nil {
		http.Error(w, "写入文件错误", http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "文件上传成功: %s (%d bytes)", handler.Filename, handler.Size)
}

func listFilesHandler(w http.ResponseWriter, r *http.Request) {
	files, err := os.ReadDir(uploadDir)
	if err != nil {
		http.Error(w, "服务器错误", http.StatusInternalServerError)
		return
	}

	fmt.Fprintln(w, "已上传的文件:")
	for _, file := range files {
		fmt.Fprintf(w, "- %s\n", file.Name())
	}
}

func main() {
	// 设置路由
	http.HandleFunc("/upload", uploadHandler)
	http.HandleFunc("/list", listFilesHandler)

	// 创建上传目录
	if err := os.MkdirAll(uploadDir, os.ModePerm); err != nil {
		fmt.Printf("创建上传目录错误: %v\n", err)
		return
	}

	fmt.Println("文件上传服务已启动,访问 http://localhost:8083")
	fmt.Println("使用 /upload 端点上传文件")
	fmt.Println("使用 /list 端点查看已上传文件")

	// 启动服务器
	if err := http.ListenAndServe(":8083", nil); err != nil {
		fmt.Printf("服务器错误: %v\n", err)
	}
}
#文件目录操作 #Os库 #Golang