Advertisement
  1. Code
  2. Go

与Go同行:Golang的命令行程序

by
Read Time:7 minsLanguages:

Chinese (Simplified) (中文(简体)) translation by Qiang Ji (you can also view the original English article)

概述

Go语言是一种令人兴奋的新语言,它能获得了很多人气是有一定原因的。 在本篇教程中你将学到如何用Go语言编写命令行程序。 示例程序叫做multi-git,它允许你在同一时间对多个git存储库执行git命令。

Go快速介绍

Go是一个开源的类似C语言的语言,它在Google被一些C语言和Unix的骇客创建,而动机则是他们不喜欢C++语言。 这表现在Go的设计中,它做了几个非正统的选择,例如避开继承的实现,模板的实现和异常的实现。 Go是简单的,可靠的和有效的。 其最显著的特点是通过所谓的goroutines和管道(channel)直接地支持并发编程。

在开始剖析示例程序之前,请按照官方指南为Go开发做好准备。

Multi-Git程序

Multi-git程序是一个简单但有用的Go程序。 如果你与一队人马工作,代码库被分成多个相互联系的代码存储库。那么你常常需要更新代码库。 这里有个问题,因为git没有支持多个代码库的概念。 一切都围绕着一个存储库。

如果你使用分支,这会变得特别麻烦。 如果你开发一个功能需要涉及三个存储库,那么你将不得不在每个存储库中创建一个功能分支,然后记住同时检出,拉,推,并合并所有这些。 这并不简单。 Multi-git管理一组存储库和能够让你立即对整组存储库操作。 注意multi-git的当前版本需要你单独地创建分支,但我可以迟点加入这个功能。

通过探索multi-git的实现方式,你将学习到很多关于编写Go命令行程序的知识。

包和导入

Go程序是用包组织的。 multi-git程序是由一个单个的叫main.go的文件组成的。 在文件的顶部,指定包名称“main”,然后是一个导入列表。 这些被导入的是会被multi-git程序使用到的其它包。

例如,fmt包是用来格式化I/O的,这与C语言的printf和scanf类似。 Go支持通过go get命令从各种来源安装包。 当你安装包时,它们以把它们安装在$GOPATH环境变量的名称空间下结束。 你可以经由一些通用的版本控制格式比如git, subversion, mercurial和bazaar,从各种来源比如GitHub, Bitbucket, Google code, Launchpad,甚至IBM DevOps服务来安装包。

命令行参数

命令行参数是向程序提供输入的最常见形式之一。 它们易于使用,允许你在一行命令中运行和配置程序,并在多种语言中都有很棒的解析支持。 Go称它们命令行“标识”并且有指定和解析命令行参数(或标识)的标识包。

通常,你在程序开始时解析命令行参数,multi-git程序遵循这个约定。 进入点是main()函数。 前两行代码定义了两个标识,名为“command”和“ignoreErrors”。 每个标识有一个名字,一个数据结构,一个默认值, 和一个描述字符串。 flag.Parse()的调用将解析实际的传给程序的命令行并且定义的标识。

也可以通过flag.Args()函数访问未定义的参数。 因此,标识代表预定义的参数,“args”是未处理的参数。 未处理的参数是基于0的索引。

环境变量

另一种程序配置的通用形式是环境变量。 当你使用环境变量时,你也许会在同样的环境中运行相同的程序多次,所有的程序运行将使用相同的环境变量。

multi-git使用两个环境变量:"MG_ROOT"和"MG_REPOS"。 Multi-git旨在管理一组具有公共父目录的git存储库。 那就是"MG_ROOT"。 存储库名作为一个以逗号分隔的字符串在"MG_REPOS"环境变量中被指定。 你可以使用os.Getenv()函数读取一个环境变量的值。

验证存储库列表

现在我们有了根目录和所有存储库的名字,multi-git验证每个在根目录下的存储库都存在并且的确是一个git存储库。 这个验证就是简单的看一下每个存储库目录的一个.git子目录。

首先,一个名字叫“repo”的字符串数组被定义。 然后它遍历所有存储库的名字并且通过拼接根目录和存储库名建立一个存储库路径。 如果针对.git子目录的[os.State()]()调用失败,它把错误写入日志并且退出。 否则,存储库路径会被加到存储库数组的最后。

Go有一个独特的错误处理设施就是函数通常返回一个返回值和一个出错对象。 看一下os.State()是如何返回两个值的。 "_"占位符被用来存储真正的返回结果因为在这种情况下你只关心返回的错误。 Go是非常严谨的并且需要使用有名字的变量。 如果你没有计划去使用一个值,你应该给它赋值"_"去避免编译错位。

执行Shell命令

在此刻,你有我们想执行git命令的你的存储库路径的目录。 你回想一下,我们接受一个作为单独命令行参数(标识)叫做"command"的git命令行。 这需要被分开并存入一个组件的数组(git命令,子命令,和选项)。 完整的命令作为一个字符串也被保存以为显示用。

现在,你对遍历每个存储库并且在每个存储库中执行git命令都已配置好 "for ... range"循环架构被再次使用。 首先,multi-git把它的工作目录改变为当前目标存储库"r"并且打印出git命令。 然后它用exec.Command()函数执行命令并且打印出合并的输出。

最后,它检查在执行过程中是否有报错。 如果有报错并且ignoreErrors标识是假值,那么multi-git程序安全终止。 有选择地忽略报错的原因是有时针对一些存储库的命令失败是可以接受的 例如,如果你想查看一个在所有存储库都有的名叫"cool feature"的分支,你不会在意那些没有这个分支的存储库的查看失败。

结论

Go是一个简单但强大的语言。 它是专为大规模系统编程设计的,但对小型命令行程序也是适用的。 Go的简朴设计与其它代语言(如Scale和Rust)形成鲜明对比,这些现代语言非常强大,设计精良,但具有非常陡峭的学习曲线。 我鼓励你们去试试。 你会找到很多乐趣。

关注我们的公众号
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.