下载了一个安装包,怎么确认文件没有被篡改?手动比对 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 version 确认)打开终端,在同目录下对 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,从一个小工具开始试吧。