总览
Go语言是一种令人兴奋的新语言,它有很好的理由而获得了广泛的欢迎。 在本教程中,您将学习如何使用Go编写命令行程序。 该示例程序称为multi-git,它使您可以同时在多个存储库上执行git命令。
快速入门
Go是一种由C语言和Unix最初的黑客在Google创建的类似于C语言的开源语言,他们是由对C ++的厌恶所激发的。 它显示在Go的设计中,该设计做出了一些非常规的选择,例如避免实现继承,模板和异常。 Go简单,可靠且高效。 它最独特的功能是通过所谓的goroutine和通道显式支持并发编程。
在开始剖析示例程序之前,请遵循官方指南为Go开发做好准备。
Multi-Git程序
multi-git程序是一个简单但有用的Go程序。 如果您的团队的代码库分散在多个git存储库中,则通常需要在多个存储库中执行更改。 这是一个问题,因为git没有多个存储库的概念。 一切都围绕一个单一的存储库。
如果使用分支,这将变得特别麻烦。 如果您使用的功能涉及三个存储库,则必须在每个存储库中创建一个功能分支,然后记住同时签出,拉出,推送和合并所有存储库。 这不是小事。 Multi-git管理一组存储库,并允许您一次对整个存储库进行操作。 请注意,当前版本的multi-git要求您单独创建分支,但是我可能会在以后添加此功能。
通过探索实现multi-git的方式,您将学到很多有关在Go中编写命令行程序的知识。
包装和进口
Go程序以程序包形式组织。 multi-git程序包含一个名为main.go的文件。 在文件的顶部,指定包名称“ main”,然后是导入列表。 导入是multi-git使用的其他软件包。
package main
import (
"flag"
"fmt"
"log"
"os"
"strings"
"os/exec"
)
go get
命令行参数
命令行参数是向程序提供输入的最常见形式之一。 它们易于使用,可让您在一行中运行和配置程序,并具有多种语言的强大解析支持。 Go将其称为命令行“标志”,并具有用于指定和解析命令行参数(或标志)的标志包。
main()flag.Parse()
func main() {
command := flag.String("command", "", "The git command")
ignoreErrors := flag.Bool(
"ignore-errors",
false,
"Keep running after error if true")
flag.Parse()
flag.Args()
环境变量
程序配置的另一种常见形式是环境变量。 使用环境变量时,可能会在同一环境中多次运行同一程序,并且所有运行都将使用同一环境变量。
os.Getenv()
// Get managed repos from environment variables
root := os.Getenv("MG_ROOT")
if root[len(root) - 1] != '/' {
root += "/"
}
repo_names := strings.Split(os.Getenv("MG_REPOS"), ",")
验证存储库列表
现在,它找到了根目录和所有存储库的名称,multi-git验证每个存储库都位于root下,并且它确实是一个git存储库。 检查就像在每个存储库目录中查找.git子目录一样简单。
[os.Stat()]()
var repos []string
// Verify all repos exist and are actually git repos (have .git sub-dir)
for _, r := range repo_names {
path := root + r
_, err := os.Stat(path + "/.git")
if err != nil {
log.Fatal(err)
}
repos = append(repos, path)
}
os.Stat()
执行Shell命令
至此,您已经有了我们要执行git命令的存储库路径列表。 您还记得,我们将git命令行作为单个命令行参数(标志)(称为“ command”)接收到。 需要将其拆分为组件数组(git命令,子命令和选项)。 整个命令也以字符串形式存储,以用于显示。
// Break the git command into components (needed to execute)
var git_components []string
for _, component := range strings.Split(*command, " ") {
git_components = append(git_components, component)
}
command_string := "git " + *command
exec.Command()
ignoreErrors
for _, r := range repos {
// Go to the repo's directory
os.Chdir(r);
// Print the command
fmt.Printf("[%s] %s\n", r, command_string)
// Execute the command
out, err := exec.Command("git", git_components...).CombinedOutput()
// Print the result
fmt.Println(string(out))
// Bail out if there was an error and NOT ignoring errors
if err != nil && !*ignoreErrors {
os.Exit(1)
}
}
fmt.Println("Done.")
结论
Go是一种简单而强大的语言。 它是为大型系统编程而设计的,但对于小型命令行程序也同样适用。 Go的最小化设计与其他现代语言(例如Scale和Rust)形成了鲜明的对比,Scale和Rust也非常强大且设计合理,但是学习曲线非常陡峭。 我鼓励您尝试去尝试。 其乐无穷。