总览

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也非常强大且设计合理,但是学习曲线非常陡峭。 我鼓励您尝试去尝试。 其乐无穷。