2
原文链接:https://idoubi.cc/posts/create-a-cli-tool-in-macos/

使用 MacOS 做开发的朋友都知道,我们一般会使用 Homebrew 做软件包管理,经常会用到 brew install [soft] 来安装各种各样的命令行软件。今天通过一个百科查找的命令行工具(tellme)示例,我们来学习一下如何使用 Go 语言开发自己的命令行软件。

我们需要用到 cobra 这个 Go 模块来做命令行工具开发,这个开源库其实是对 Go 官方库 flag 的一个封装,可以简化获取参数的操作。

创建命令行项目

  • 开启 Go Module
export GO111MODULE=on
  • 安装 cobra 工具
go get -u github.com/spf13/cobra/cobra
  • 创建命令行项目
# 创建项目目录
mkdir -p /data/idoubi/tellme && cd /data/idoubi/tellme

# 定义模块
go mod init github.com/idoubi/tellme

# 初始化命令行项目
cobra init --pkg-name github.com/idoubi/tellme
  • 检测运行
go run main.go -h

执行完上述操作后,如果控制台输出了帮助信息,证明我们的命令行项目创建成功了。

新建子命令

  • 新建子命令
cobra add baike
  • 编写业务逻辑

在生成的子命令文件 /data/idoubi/tellme/cmd/baike.go 中编写子命令需要实现的业务逻辑。

package cmd

import (
    "fmt"
    "os"
    "os/exec"
    "runtime"

    "github.com/spf13/cobra"
)

var (
    platform string
)

var openCmds = map[string]string{
    "windows": "cmd /c start",
    "darwin":  "open",
    "linux":   "xdg-open",
}

var baikeCmd = &cobra.Command{
    Use:     "baike",
    Aliases: []string{"bk", "wk", "wiki"},
    Short:   "find things in baike site",
    Args:    cobra.ExactArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
        err := findInBaike(args[0], platform)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    },
}

func init() {
    rootCmd.AddCommand(baikeCmd)
    baikeCmd.Flags().StringVarP(&platform, "platform", "p", "baidu", "platform to find things")
}

// 百科查找
func findInBaike(keyword, platform string) error {
    var link string
    // 百度百科搜索
    if platform == "baidu" || platform == "bd" {
        link = fmt.Sprintf("https://baike.baidu.com/item/%s", keyword)
    }
    // 互动百科搜索
    if platform == "hudong" || platform == "baike" || platform == "hd" {
        link = fmt.Sprintf("http://www.baike.com/wiki/%s", keyword)
    }
    // 维基百科搜索
    if platform == "wikipedia" || platform == "wiki" || platform == "wp" {
        link = fmt.Sprintf("https://zh.wikipedia.org/wiki/%s", keyword)
    }
    if link == "" {
        return fmt.Errorf("invalid platform")
    }
    goos := runtime.GOOS
    opencmd := "open"
    opencmd, ok := openCmds[goos]
    if !ok {
        return fmt.Errorf("can not open link in %s", goos)
    }
    if err := exec.Command(opencmd, link).Start(); err != nil {
        return err
    }

    return nil
}

命令测试

  • 项目中运行

在命令行项目目录下,通过 go run 可以直接运行,查看输出结果。

# 在百度百科查看信息
go run main.go baike 周杰伦

# 在维基百科查看信息
go run main.go bk -p wp 周杰伦

上述的 baike 是我们创建的子命令,bk 是子命令别名,-p 是子命令标识,用于指定百科平台。周杰伦 是接收的参数。

  • 编译运行

在命令行项目目录下,通过 go build 可以将项目编译成一个二进制文件,通过 ./tellme 可以直接运行二进制文件查看输出。

# 编译
go build -o tellme

# 运行
./tellme -h
  • 交叉编译

除了在 MacOS 中使用外,我们还希望能在 linux 和 windows 系统中使用我们的命令行工具。这种情况下我们需要用到交叉编译。

# 下载交叉编译工具
go get -u github.com/mitchellh/gox

# 编译成指定平台的可执行文件
gox -osarch "windows/amd64 linux/amd64 darwin/amd64"
  • 全局执行

编译完成后,我们可以创建一个软连接,让命令行工具可以在任何路径下全局执行。

# 创建软连接
ln -s /data/idoubi/tellme/tellme_darwin_amd64 /usr/local/bin/tm

# 全局执行
tm -h

上传到 Github

经过上述的几个步骤,我们已经完成了一个简单的命令行工具的编写与测试工作,接下来需要把软件发布出去让所有人使用。

# 推送代码到github
cd /data/idoubi/tellme
go init
git add .
git commit -m "a cli tool used in macos"
git push origin master

# 发布一个版本
git tag -a v0.1.0 -m "first version"
git push origin --tags

上传到 Github 完成后,我们可以在项目的 release 页面看到这个项目的版本信息和源码下载文件 https://github.com/idoubi/tellme/releases/tag/v0.1.0,我们可以编辑发布信息,上传交叉编译后得到的各个平台的二进制文件,方便不同平台的用户在这里直接下载二进制文件来使用我们的软件。

创建 Homebrew 软件

对于 MacOS 用户,一般都是使用 Homebrew 来安装和管理各种软件,我们希望把自己开发的命令行软件发布到 Homebrew,让用户可以方便的使用 brew install tellme 来安装。

  • 创建 Homebrew 软件

我们可以在 Github 上复制软件源码包下载地址,在 MacOS 通过 brew create [source] 创建 Homebrew 软件包。

brew create https://github.com/idoubi/tellme/archive/v0.1.0.tar.gz

执行上述命令完成后,会在 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/ 目录生成一个 tellme.rb 文件,即我们的 Homebrew 软件定义文件,在此文件中定义了我们的软件在 MacOS 中要怎么安装。

  • 编辑软件定义文件

因为我们这个命令行软件是用 Go 语言编写的,在 Homebrew 软件定义文件中,我们可以指定软件的安装依赖于 go:build,并且定义好 install 方式。

class Tellme < Formula
  desc "a cli tool to get information."
  homepage "https://github.com/idoubi/tellme"
  url "https://github.com/idoubi/tellme/archive/v0.1.0.tar.gz"
  sha256 "b0c14c0e9f02e065917262a7c0d16e205097cc4d10c01e03ad93b4cd737cf81d"

  depends_on "go" => :build

  def install
    system "go", "build", "-o", bin/"tellme"
  end

  test do
    system "false"
  end
end
  • 通过可执行文件创建 Homebrew 软件

上面看到的是通过 Go 源码编译的方式定义 Homebrew 软件的安装方式,我们还可以通过下载可执行二进制的方式定义 Homebrew 软件包的安装,比如通过交叉编译生成可执行文件。

基于可执行文件创建 Homebrew 软件包:

brew create https://github.com/idoubi/tellme/releases/download/v0.1.0/tellme_darwin_amd64.tar.gz

编辑 Homebrew 软件包的定义文件 tellme.rb:

class Tellme < Formula
  desc "a cli tool to get information"
  homepage ""
  url "https://github.com/idoubi/tellme/releases/download/v0.1.0/tellme_darwin_amd64.tar.gz"
  sha256 "164787b02050faef0d5776ca86d27d15b26fe28493f95ba2e705ba2e30026c94"

  def install
    bin.install "tellme_darwin_amd64" => "tellme"
  end

  test do
    system "false"
  end
end
  • 安装 Homebrew 软件

创建完 Homebrew 软件后,我们通过 brew install tellme 就可以基于本地生成的/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/tellme.rb 文件来安装我们的命令行软件了。

如果安装报错了,可以通过 brew edit tellme 继续编辑 Homebrew 软件定义文件。

发布 Homebrew 软件

经过上面的步骤,我们创建了一个在 MacOS 通过 Homebrew 安装的软件,接下来我们需要把这个软件发布出去,让用户能够通过简单的 brew 命令直接安装使用。

  • 发布到 Github 仓库
# 创建软件仓库
mkdir -p /data/idoubi/homebrew-tools && cd /data/idoubi/homebrew-tools

# 复制软件定义文件到仓库目录
cp /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/tellme.rb /data/idoubi/homebrew-tools/

# 推送到 Github 仓库
git init
git remote add origin https://github.com/idoubi/homebrew-tools.git
git add .
git commit -m "a homebrew soft named tellme"
git push origin master
  • 安装软件

软件定义文件推送到 Github 仓库后,可以删除本地的 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/tellme.rb 文件。

任何人可以通过下面两行命令在自己的 MacOS 系统中使用我们开发的命令行软件。

# 连接软件仓库
brew tap idoubi/tools
# 从软件仓库安装软件
brew install tellme

总结

前面的内容简单介绍了如何使用 Go 语言来创建一个在 MacOS 使用的命令行软件,需要开发者熟悉 MacOS 中 Homebrew 软件包管理工具的使用以及 Go 语言。后面我们可以展开更多的想象,在我们的命令行工具中添加查天气、搜歌词、翻译、时间类型转换等各种特性功能。

  • 源码

命令行软件源码:tellme

Homebrew仓库:homebrew-tools

参考


艾逗笔
61 声望3 粉丝

有逻辑的脑子万里挑一。