OpenCode 实践课:3 分钟用 Go 写出一个命令行文件哈希校验工具

项目介绍

下载了一个安装包,怎么确认文件没有被篡改?手动比对 MD5 太麻烦,打开在线工具又担心隐私。今天我们用 OpenCode 在 3 分钟内写一个命令行哈希校验工具,支持 MD5、SHA1、SHA256、SHA512 四种算法,既支持文件输入也支持管道输入。

技术栈:Go 1.26,纯标准库,零外部依赖。最终代码 76 行。

最终效果:

$ hashcheck -algo sha256 下载的文件.exe
sha256  b9dfef237ed0f09ab2c98c1705968017b6c9f46dc434272082fc3d04f0a6f6ca

$ echo "hello world" | hashcheck -algo md5
md5  6f5902ac237024bdd0c176cb93063dc4

准备工作

  • Go 1.21+ 已安装(go version 确认)
  • OpenCode 已安装并配置好 API Key

实践过程

第一步:搭建项目骨架

打开终端,在同目录下对 OpenCode 说:

> 创建一个 Go 项目 hashcheck,用 go mod init。写一个 main.go,实现命令行哈希校验工具:支持 -algo 参数选择 md5/sha1/sha256/sha512,接收文件路径作为参数,输出 "算法名 哈希值"。

OpenCode 首先创建模块,然后生成核心代码——用 crypto/* 标准库包,map[string]func() hash.Hash 来做算法注册表,flag 包处理命令行参数。代码结构很清晰,每个算法通过工厂函数动态创建,不需要写一堆 switch-case

生成的代码约 40 行,已经可以工作。执行 go build,一次性编译通过。

第二步:增加管道输入支持

基础功能跑通后,我继续提需求:

> 如果命令行没有提供文件路径,改为从 stdin 读取,支持管道输入。如果既没有文件也没有管道(用户直接回车),就打印用法帮助。

这次 OpenCode 的修改很巧妙。它先用 os.Stdin.Stat() 检查标准输入的状态——如果 ModeCharDevice 未设置,说明有管道数据;否则显示帮助信息。这个判断逻辑在很多 CLI 工具里都能看到,OpenCode 直接帮我们实现了,不需要自己去查文档。

我测试了两个场景:

# 管道输入
$ echo "hello" | hashcheck -algo sha256

# 直接回车——显示帮助
$ hashcheck
用法: hashcheck [-algo sha256] <文件路径>

都符合预期。

第三步:完善错误处理与用户体验

工具已经能用了,但用户体验还可以打磨。我对 OpenCode 说:

> 增加健壮的错误处理:文件不存在时给出友好提示;不支持 -algo 参数给出的无效算法时提示可选值。另外在帮助信息里加上管道用法的示例。

OpenCode 立刻理解了需求。它给文件打开、数据读取、算法校验都加了 fmt.Fprintf(os.Stderr, ...) 风格的错误输出,遇到错误直接 os.Exit(1) 退出。帮助信息的管道示例也自动加上去了。

我对代码风格提出偏好:

> 换一种更简洁的错误处理方式,把文件读取和 stdin 判断包装成一个函数。

但这次 OpenCode 建议保持原样——因为 76 行的工具抽函数反而会让代码跳来跳去,可读性不升反降。我接受了这个建议。OpenCode 不是盲目执行,它会根据实际情况做出判断,这一点比简单的代码生成器强得多。

第四步:最终测试

我对 OpenCode 说:

> 运行几个测试用例验证所有功能:文件输入、stdin 输入、无参数帮助、无效算法报错。

OpenCode 自动执行了一组测试:

# 测试 1:管道 + SHA256
echo "hello world" | hashcheck -algo sha256

# 测试 2:管道 + MD5
echo "hello world" | hashcheck -algo md5

# 测试 3:文件输入
hashcheck hashcheck.exe -algo sha256

# 测试 4:无参数——显示帮助
hashcheck

全部通过,输出结果可以用 sha256sum 或在线工具交叉验证。

完整代码

package main

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"strings"
)

var algorithms = map[string]func() hash.Hash{
	"md5":    md5.New,
	"sha1":   sha1.New,
	"sha256": sha256.New,
	"sha512": sha512.New,
}

func main() {
	algo := flag.String("algo", "sha256", "hash algorithm: md5, sha1, sha256, sha512")
	flag.Parse()

	if *algo != "" {
		*algo = strings.ToLower(*algo)
	}

	newHash, ok := algorithms[*algo]
	if !ok {
		fmt.Fprintf(os.Stderr, "不支持的哈希算法: %s\n支持的算法: md5, sha1, sha256, sha512\n", *algo)
		os.Exit(1)
	}

	var reader io.Reader
	args := flag.Args()

	if len(args) > 0 {
		file, err := os.Open(args[0])
		if err != nil {
			fmt.Fprintf(os.Stderr, "无法打开文件: %v\n", err)
			os.Exit(1)
		}
		defer file.Close()
		reader = file
	} else {
		stat, _ := os.Stdin.Stat()
		if (stat.Mode() & os.ModeCharDevice) == 0 {
			reader = os.Stdin
		} else {
			fmt.Println("用法: hashcheck [-algo sha256] <文件路径>")
			fmt.Println("支持管道输入: cat 文件 | hashcheck -algo md5")
			flag.PrintDefaults()
			os.Exit(0)
		}
	}

	h := newHash()
	if _, err := io.Copy(h, reader); err != nil {
		fmt.Fprintf(os.Stderr, "读取数据失败: %v\n", err)
		os.Exit(1)
	}

	hashStr := fmt.Sprintf("%x", h.Sum(nil))
	fmt.Printf("%s  %s\n", *algo, hashStr)
}

小结

整个过程,我从项目初始化到功能完善再到测试验证,总共对 OpenCode 说了 4 句话,耗时约 3 分钟。

对比传统开发方式:写一个哈希校验工具,即便用 Go 标准库,你也得查 crypto/sha256 的 API 签名、回忆 io.Copy 的用法、搞清楚 os.Stdin.Stat() 怎么判断管道模式……这些零碎的知识点查询会消耗大量时间。OpenCode 帮你一次性生成正确的代码,你只需要关注"要做什么"而不是"怎么写"。

值得注意的一点:OpenCode 并非言听计从。当我要求把代码拆成更多函数时,它给出了合理的反对意见——小工具过度抽取函数反而不利于阅读。这种"有主见的助手"比单纯生成代码的工具有价值得多。

如果你也想体验这种开发效率,打开终端,装好 OpenCode,从一个小工具开始试吧。