Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 实战篇

    • 🚀 实战篇
    • 第1个项目 - 命令行文件管理器
    • 第2个项目 - RESTful API Todo服务
    • 第3个项目 - Web爬虫 新闻采集器
    • 第4个项目 - 实时聊天室 WebSocket
    • 第5个项目 - URL短链接服务
    • 第6个项目 - 完整博客系统

第1个项目 - 命令行文件管理器

嘿,朋友们!我是长安。

今天我们要做的第一个实战项目是命令行文件管理器(CLI File Manager)。这是一个非常实用的工具,可以帮你快速查看、搜索、复制、删除文件。

说实话,这个项目虽然简单,但涵盖了Go开发中最基础也最重要的技能:文件操作、命令行参数解析、错误处理。我当年自学Go的时候,也是从这种小工具开始练手的。掌握它,你就能独立开发各种命令行工具了!

🎯 项目目标

实现一个命令行工具,支持以下功能:

命令功能示例
list列出目录下所有文件filemanager list ./
search搜索包含关键词的文件filemanager search "test" ./
copy复制文件或目录filemanager copy src.txt dst.txt
delete删除文件或目录filemanager delete old.txt
tree以树形结构显示目录filemanager tree ./

📁 项目结构

cli-file-manager/
├── main.go              # 程序入口
├── commands/
│   ├── list.go         # list 命令实现
│   ├── search.go       # search 命令实现
│   ├── copy.go         # copy 命令实现
│   ├── delete.go       # delete 命令实现
│   └── tree.go         # tree 命令实现
├── utils/
│   └── fileutil.go     # 文件工具函数
└── go.mod

🚀 第一步:项目初始化

创建项目目录并初始化Go模块:

# 创建项目目录
mkdir cli-file-manager
cd cli-file-manager

# 初始化Go模块
go mod init github.com/yourusername/cli-file-manager

# 创建目录结构
mkdir commands utils
touch main.go
touch commands/list.go commands/search.go commands/copy.go commands/delete.go commands/tree.go
touch utils/fileutil.go

💻 第二步:实现主程序

文件: main.go

package main

import (
	"fmt"
	"os"

	"github.com/yourusername/cli-file-manager/commands"
)

func main() {
	// 检查命令行参数
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}

	// 获取命令
	command := os.Args[1]

	// 根据命令执行相应操作
	switch command {
	case "list":
		commands.List(os.Args[2:])
	case "search":
		commands.Search(os.Args[2:])
	case "copy":
		commands.Copy(os.Args[2:])
	case "delete":
		commands.Delete(os.Args[2:])
	case "tree":
		commands.Tree(os.Args[2:])
	default:
		fmt.Printf("未知命令: %s\n", command)
		printUsage()
		os.Exit(1)
	}
}

// 打印使用说明
func printUsage() {
	fmt.Println("📁 文件管理器 - 命令行工具")
	fmt.Println("\n使用方法:")
	fmt.Println("  filemanager <command> [arguments]")
	fmt.Println("\n可用命令:")
	fmt.Println("  list <path>              列出目录下所有文件")
	fmt.Println("  search <keyword> <path>  搜索包含关键词的文件")
	fmt.Println("  copy <src> <dst>         复制文件或目录")
	fmt.Println("  delete <path>            删除文件或目录")
	fmt.Println("  tree <path>              以树形结构显示目录")
	fmt.Println("\n示例:")
	fmt.Println("  filemanager list ./")
	fmt.Println("  filemanager search \"test\" ./")
	fmt.Println("  filemanager copy src.txt dst.txt")
}

📋 第三步:实现 list 命令

文件: commands/list.go

package commands

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

// List 列出目录下所有文件和子目录
func List(args []string) {
	// 检查参数
	if len(args) < 1 {
		fmt.Println("❌ 错误: 请指定目录路径")
		fmt.Println("用法: filemanager list <path>")
		return
	}

	path := args[0]

	// 读取目录内容
	entries, err := os.ReadDir(path)
	if err != nil {
		fmt.Printf("❌ 读取目录失败: %v\n", err)
		return
	}

	// 打印目录信息
	fmt.Printf("📁 目录: %s\n", path)
	fmt.Printf("共 %d 项:\n\n", len(entries))

	// 遍历并打印每一项
	for _, entry := range entries {
		// 获取文件信息
		info, err := entry.Info()
		if err != nil {
			fmt.Printf("⚠️  获取信息失败: %s\n", entry.Name())
			continue
		}

		// 根据类型显示不同图标
		icon := "📄"
		if entry.IsDir() {
			icon = "📁"
		}

		// 格式化文件大小
		size := formatSize(info.Size())

		// 打印文件信息
		fmt.Printf("%s %-40s %10s %s\n",
			icon,
			entry.Name(),
			size,
			info.ModTime().Format("2006-01-02 15:04:05"),
		)
	}
}

// formatSize 格式化文件大小
func formatSize(size int64) string {
	const unit = 1024
	if size < unit {
		return fmt.Sprintf("%d B", size)
	}
	div, exp := int64(unit), 0
	for n := size / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(size)/float64(div), "KMGTPE"[exp])
}

🔍 第四步:实现 search 命令

文件: commands/search.go

package commands

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

// Search 搜索包含关键词的文件
func Search(args []string) {
	// 检查参数
	if len(args) < 2 {
		fmt.Println("❌ 错误: 参数不足")
		fmt.Println("用法: filemanager search <keyword> <path>")
		return
	}

	keyword := args[0]
	searchPath := args[1]

	fmt.Printf("🔍 在 %s 中搜索包含 \"%s\" 的文件...\n\n", searchPath, keyword)

	// 用于统计找到的文件数
	count := 0

	// 遍历目录树
	err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil // 忽略错误,继续搜索
		}

		// 跳过目录
		if info.IsDir() {
			return nil
		}

		// 检查文件名是否包含关键词
		if strings.Contains(strings.ToLower(info.Name()), strings.ToLower(keyword)) {
			count++
			size := formatSize(info.Size())
			fmt.Printf("✅ %s (%s)\n", path, size)
		}

		return nil
	})

	if err != nil {
		fmt.Printf("❌ 搜索失败: %v\n", err)
		return
	}

	fmt.Printf("\n📊 共找到 %d 个文件\n", count)
}

📄 第五步:实现 copy 命令

文件: commands/copy.go

package commands

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

// Copy 复制文件或目录
func Copy(args []string) {
	// 检查参数
	if len(args) < 2 {
		fmt.Println("❌ 错误: 参数不足")
		fmt.Println("用法: filemanager copy <src> <dst>")
		return
	}

	src := args[0]
	dst := args[1]

	// 获取源文件信息
	srcInfo, err := os.Stat(src)
	if err != nil {
		fmt.Printf("❌ 源文件不存在: %v\n", err)
		return
	}

	// 根据文件类型选择复制方法
	if srcInfo.IsDir() {
		err = copyDir(src, dst)
	} else {
		err = copyFile(src, dst)
	}

	if err != nil {
		fmt.Printf("❌ 复制失败: %v\n", err)
		return
	}

	fmt.Printf("✅ 成功复制: %s -> %s\n", src, dst)
}

// copyFile 复制单个文件
func copyFile(src, dst string) error {
	// 打开源文件
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	// 创建目标文件
	dstFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// 复制内容
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	// 复制文件权限
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}
	return os.Chmod(dst, srcInfo.Mode())
}

// copyDir 递归复制目录
func copyDir(src, dst string) error {
	// 获取源目录信息
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	// 创建目标目录
	err = os.MkdirAll(dst, srcInfo.Mode())
	if 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() {
			err = copyDir(srcPath, dstPath)
		} else {
			err = copyFile(srcPath, dstPath)
		}

		if err != nil {
			return err
		}
	}

	return nil
}

🗑️ 第六步:实现 delete 命令

文件: commands/delete.go

package commands

import (
	"fmt"
	"os"
)

// Delete 删除文件或目录
func Delete(args []string) {
	// 检查参数
	if len(args) < 1 {
		fmt.Println("❌ 错误: 请指定要删除的文件或目录")
		fmt.Println("用法: filemanager delete <path>")
		return
	}

	path := args[0]

	// 确认是否删除
	fmt.Printf("⚠️  确定要删除 %s 吗? (y/n): ", path)
	var confirm string
	fmt.Scanln(&confirm)

	if confirm != "y" && confirm != "Y" {
		fmt.Println("❌ 已取消删除")
		return
	}

	// 删除文件或目录
	err := os.RemoveAll(path)
	if err != nil {
		fmt.Printf("❌ 删除失败: %v\n", err)
		return
	}

	fmt.Printf("✅ 成功删除: %s\n", path)
}

🌳 第七步:实现 tree 命令

文件: commands/tree.go

package commands

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

// Tree 以树形结构显示目录
func Tree(args []string) {
	// 检查参数
	if len(args) < 1 {
		fmt.Println("❌ 错误: 请指定目录路径")
		fmt.Println("用法: filemanager tree <path>")
		return
	}

	path := args[0]

	fmt.Printf("📁 %s\n", path)
	printTree(path, "", true)
}

// printTree 递归打印目录树
func printTree(path string, prefix string, isLast bool) {
	// 读取目录内容
	entries, err := os.ReadDir(path)
	if err != nil {
		return
	}

	// 遍历每一项
	for i, entry := range entries {
		isLastItem := i == len(entries)-1

		// 打印当前项
		if isLastItem {
			fmt.Printf("%s└── ", prefix)
		} else {
			fmt.Printf("%s├── ", prefix)
		}

		// 根据类型显示不同图标
		icon := "📄"
		if entry.IsDir() {
			icon = "📁"
		}
		fmt.Printf("%s %s\n", icon, entry.Name())

		// 如果是目录,递归打印子目录
		if entry.IsDir() {
			newPrefix := prefix
			if isLastItem {
				newPrefix += "    "
			} else {
				newPrefix += "│   "
			}
			printTree(filepath.Join(path, entry.Name()), newPrefix, isLastItem)
		}
	}
}

🔧 第八步:编译和测试

编译程序

# 编译为可执行文件
go build -o filemanager main.go

# Windows用户
go build -o filemanager.exe main.go

测试各个命令

# 1. 列出当前目录
./filemanager list ./

# 2. 搜索文件
./filemanager search "go" ./

# 3. 复制文件
echo "Hello, Go!" > test.txt
./filemanager copy test.txt test_copy.txt

# 4. 显示目录树
./filemanager tree ./

# 5. 删除文件
./filemanager delete test_copy.txt

📊 运行效果

$ ./filemanager list ./

📁 目录: ./
共 8 项:

📁 commands                                     4.0 KB 2025-12-27 10:30:00
📁 utils                                        4.0 KB 2025-12-27 10:30:00
📄 main.go                                      1.2 KB 2025-12-27 10:25:00
📄 go.mod                                        120 B 2025-12-27 10:20:00
📄 filemanager                                  2.1 MB 2025-12-27 10:35:00

$ ./filemanager search "list" ./

🔍 在 ./ 中搜索包含 "list" 的文件...

✅ commands/list.go (2.3 KB)

📊 共找到 1 个文件

💡 扩展思考

完成基础功能后,你可以尝试以下扩展:

1. 添加更多命令

  • rename - 重命名文件
  • mkdir - 创建目录
  • info - 显示文件详细信息
  • find - 按文件类型或大小查找

2. 改进用户体验

  • 添加彩色输出(使用 github.com/fatih/color 包)
  • 添加进度条(复制大文件时)
  • 支持通配符匹配(如 *.go)

3. 增强功能

  • 支持正则表达式搜索
  • 添加文件内容搜索(类似 grep)
  • 支持压缩和解压缩
  • 添加文件监控功能

4. 使用第三方库

可以使用 github.com/spf13/cobra 来构建更专业的CLI应用:

go get -u github.com/spf13/cobra

💪 练习题

  1. 实现一个 size 命令,统计目录的总大小
  2. 添加 --recursive 参数,让 list 命令支持递归列出所有子目录
  3. 实现文件内容搜索功能,不仅搜索文件名,还搜索文件内容
  4. 添加日志记录功能,记录所有操作到日志文件
💡 参考答案
// size 命令示例
func Size(args []string) {
	if len(args) < 1 {
		fmt.Println("用法: filemanager size <path>")
		return
	}
	
	var totalSize int64
	filepath.Walk(args[0], func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}
		if !info.IsDir() {
			totalSize += info.Size()
		}
		return nil
	})
	
	fmt.Printf("总大小: %s\n", formatSize(totalSize))
}

🎯 小结

恭喜你完成了第一个实战项目!通过这个项目,你学会了:

✅ Go程序的基本结构和模块化设计
✅ 命令行参数的解析和处理
✅ 文件和目录的操作(读取、复制、删除)
✅ 错误处理的最佳实践
✅ 递归算法的应用

这些技能是Go开发的基础,后面的项目会在此基础上不断深入。

下一步: 第2个项目 - RESTful API Todo服务 →


💬 遇到问题了吗?

  • 确保Go版本是1.21+
  • 检查文件路径是否正确
  • 注意Windows和Linux的路径分隔符差异
  • 访问 编程指南 获取更多帮助
最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
🚀 实战篇
Next
第2个项目 - RESTful API Todo服务