背景:使用Goland开发,没有vscode那种保存文件后自动格式化的功能,每次都得Alt+Shift+F来格式化,而且每次只能格式化一个文件。而用Goland的Go fmt projects又会把vendor下的包也格式化了,这样又很不科学。然后不想去找插件,于是决定自己来折腾一下。预期目标是可以开发完成一个项目后,运行一条命令就能格式化项目中除vendor的所有go文件。
实现原理
golang提供了格式化文件的工具go fmt,使用方法是在控制台运行go fmt *.go,就能格式化对应的go文件。也就是说,只要我在代码里面能够获取到某个项目目录下除vendor外的所有go文件,然后对每一个go文件都执行go fmt操作即可。
具体实现
golang执行控制台命令
golang自带的os/exec包内置了对控制台命令的操作,如下
func main(){
cmd := exec.Command("go","version")
cmd.Stdout = os.Stdout
cmd.Run()
}
复制代码
其中cmd.Stdout = os.Stdout是把此次执行的命令的输出结果打印到系统的控制台。还有更多的用法这里不多解释。
获取某个文件夹下所有的go文件
思路如下:打开文件夹,遍历文件夹下所有的东西,若是go文件,则执行go fmt命令,若是文件夹,则递归一下。
func formatDir(path string) {
fileInfo, err := ioutil.ReadDir(path)
if err != nil {
fmt.Println("open directory error,", err.Error())
return
}
for _, file := range fileInfo {
fileName := path+"/"+file.Name()
if file.IsDir() {
// fmt.Println("dir :", fileName)
if file.Name() != "vendor" {
formatDir(fileName)
}
} else {
if strings.HasSuffix(file.Name(), "go") {
fmt.Println("format go file :", fileName)
goFormatFile(fileName)
}
}
}
}
func goFormatFile(fileName string) {
cmd := exec.Command("go", "fmt", fileName)
cmd.Stdout = os.Stdout
cmd.Run()
}
复制代码
加入goroutine
功能总体是完成了,但是因为本质上是一个文件一个文件地执行go fmt。其次打开文件的IO操作更是容易造成IO阻塞,那这样就有用多线程的思想来改造的价值了,于是引入go routine。
func formatDir(path string) {
fileInfo, err := ioutil.ReadDir(path)
if err != nil {
fmt.Println("open directory error,", err.Error())
return
}
for _, fi := range fileInfo {
f.Add(1)
go func(file os.FileInfo) {// 这里要注意避免直接使用for循环的引用变量fi,不然所有go routine操作的对象都是最后一次for循环取到的对象
defer f.Done()
fileName := path+"/"+file.Name()
if file.IsDir() {
// fmt.Println("dir :", fileName)
if file.Name() != "vendor" {
formatDir(fileName)
}
} else {
if strings.HasSuffix(file.Name(), "go") {
fmt.Println("format go file :", fileName)
goFormatFile(fileName)
}
}
}(fi)
}
}
复制代码
所有的程序
加点简单的错误处理和封装,加一个函数入口,代码就完成了,这里就不重复贴了。代码全放在LSivan/fmt_directory,有兴趣的观众老爷可以下载玩一下。
效果
相对路径
$ go run main -d .
format go file : ./test_cmd/main.go
main.go
复制代码
绝对路径
$ go run main.go -d $GOPATH/src/works_demo/liangxingwei/
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_goc/main.go
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_influxdb/msg_queue/queue.go
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_radix/main.go
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_redigo/main.go
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_redis/main.go
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_worker/main.go
format go file : /home/superman/workspace/go/src/works_demo/liangxingwei/test_goc/add/add.go
../test_goc/add/add.go
../test_goc/main.go
复制代码