用golang寫個格式化go檔案的小工具

liangxingwei發表於2018-06-22

背景:使用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
複製程式碼

相關文章