第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
💪 练习题
- 实现一个
size命令,统计目录的总大小 - 添加
--recursive参数,让list命令支持递归列出所有子目录 - 实现文件内容搜索功能,不仅搜索文件名,还搜索文件内容
- 添加日志记录功能,记录所有操作到日志文件
💡 参考答案
// 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的路径分隔符差异
- 访问 编程指南 获取更多帮助
