使用Go语言在MacOS创建一个自定义的命令行工具

原文链接: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

参考

Image placeholder
renjie925
未设置
  95人点赞

没有讨论,发表一下自己的看法吧

推荐文章
Go语言高级编程_1.1 Go语言创世纪

1.1Go语言创世纪 Go语言最初由Google公司的RobertGriesemer、KenThompson和RobPike三个大牛于2007年开始设计发明,设计新语言的最初的洪荒之力来自于对超级复杂

Go编程语言教程_1.2. 如何在MacOS上安装Golang?

之前,我们从在系统上安装Golang的过程开始。我们必须对Go语言是什么以及它实际上是什么有第一手的了解。Go是Google的RobertGriesemer,RobPike和KenThompson于2

能直接下载了!微软最爽命令行工具登陆Windows 10,GitHub标星已破4万6

乾明发自凹非寺 转自量子位 |公众号QbitAI微软正式放出命令行工具WindowsTerminal。这个在发布之际就引得开发者大呼“WoW!Awesome!MyGod!”,甚至引得不少人当场表态买P

爽到飞起!微软命令行工具发布!引诱开发者叛逃Mac,开源六小时冲上GitHub第二

晓查栗子乾明发自凹非寺转自量子位 |公众号QbitAIWoW!Awesome!MyGod!这是不少抱着Mac参加微软Build大会的开发者,看到命令行工具WindowsTerminal后的第一反应。随

Vue命令行工具vue-cli详解

本文将详细介绍Vue命令行工具vue-cli。概述Vue-cli是Vue官方提供的用于初始化Vue项目的脚手架工具。使用Vue-cli有以下几大优势1、Vue-cli是一套成熟的vue项目架构设计,会

Go编程语言教程_2.0. Go语言中的标识符

在编程语言中,标识符用于标识目的。换句话说,标识符是程序组件的用户定义名称。在Go语言中,标识符可以是变量名称,函数名称,常量,语句标签,程序包名称或类型。 例: packagemain import

Go语言高级编程_4.8 grpcurl工具

4.8grpcurl工具 Protobuf本身具有反射功能,可以在运行时获取对象的Proto文件。gRPC同样也提供了一个名为reflection的反射包,用于为gRPC服务提供查询。gRPC官方提供

Go语言高级编程_3.7 汇编语言的威力

3.7汇编语言的威力 汇编语言的真正威力来自两个维度:一是突破框架限制,实现看似不可能的任务;二是突破指令限制,通过高级指令挖掘极致的性能。对于第一个问题,我们将演示如何通过Go汇编语言直接访问系统调

一条SQL语句在MySQL中如何执行的

前两天发了一条SQL慢的原因有哪些,在那篇文章我没有说到优化器之类的,我觉得如果配合一条SQL是如何执行的,会更好,所以特地找了一篇。来源:JavaGuide  |作者:木木匠本篇文章会分析一个sql

Go语言高级编程_2.1.1 最简CGO程序

2.1快速入门 本节我们将通过一系列由浅入深的小例子来快速掌握CGO的基本用法。 2.1.1最简CGO程序 真实的CGO程序一般都比较复杂。不过我们可以由浅入深,一个最简的CGO程序该是什么样的呢?要

Go语言高级编程_2.2 CGO基础

2.2CGO基础 要使用CGO特性,需要安装C/C++构建工具链,在macOS和Linux下是要安装GCC,在windows下是需要安装MinGW工具。同时需要保证环境变量CGO_ENABLED被设置

Go语言高级编程_2.7 CGO内存模型

2.7CGO内存模型 CGO是架接Go语言和C语言的桥梁,它使二者在二进制接口层面实现了互通,但是我们要注意因两种语言的内存模型的差异而可能引起的问题。如果在CGO处理的跨语言函数调用时涉及到了指针的

Go语言高级编程_3.8 例子:Goroutine ID

3.8例子:GoroutineID 在操作系统中,每个进程都会有一个唯一的进程编号,每个线程也有自己唯一的线程编号。同样在Go语言中,每个Goroutine也有自己唯一的Go程编号,这个编号在pani

Go语言高级编程_4.7 pbgo: 基于Protobuf的框架

4.7pbgo:基于Protobuf的框架 pbgo是我们专门针对本节内容设计的较为完整的迷你框架,它基于Protobuf的扩展语法,通过插件自动生成rpc和rest相关代码。在本章第二节我们已经展示

Go语言高级编程_1.2 Hello, World 的革命

1.2Hello,World的革命 在创世纪章节中我们简单介绍了Go语言的演化基因族谱,对其中来自于贝尔实验室的特有并发编程基因做了重点介绍,最后引出了Go语言版的“Hello,World”程序。其实

Go语言高级编程_1.3 数组、字符串和切片

1.3数组、字符串和切片 在主流的编程语言中数组及其相关的数据结构是使用得最为频繁的,只有在它(们)不能满足时才会考虑链表、hash表(hash表可以看作是数组和链表的混合体)和更复杂的自定义数据结构

Go语言高级编程_1.4 函数、方法和接口

1.4函数、方法和接口 函数对应操作序列,是程序的基本组成元素。Go语言中的函数有具名和匿名之分:具名函数一般对应于包级的函数,是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数

Go语言高级编程_1.5 面向并发的内存模型

1.5面向并发的内存模型 在早期,CPU都是以单核的形式顺序执行机器指令。Go语言的祖先C语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有

Go语言高级编程_1.6 常见的并发模式

1.6常见的并发模式 Go语言最吸引人的地方是它内建的并发支持。Go语言并发体系的理论是C.A.RHoare在1978年提出的CSP(CommunicatingSequentialProcess,通讯

Go语言高级编程_1.7 错误和异常

1.7错误和异常 错误处理是每个编程语言都要考虑的一个重要话题。在Go语言的错误处理中,错误是软件包API和应用程序用户界面的一个重要组成部分。 在程序中总有一部分函数总是要求必须能够成功的运行。比如

Go语言高级编程_2.3 类型转换

2.3类型转换 最初CGO是为了达到方便从Go语言函数调用C语言函数(用C语言实现Go语言声明的函数)以复用C语言资源这一目的而出现的(因为C语言还会涉及回调函数,自然也会涉及到从C语言函数调用Go语

Go语言高级编程_2.4 函数调用

2.4函数调用 函数是C语言编程的核心,通过CGO技术我们不仅仅可以在Go语言中调用C语言函数,也可以将Go语言函数导出为C语言函数。 2.4.1Go调用C函数 对于一个启用CGO特性的程序,CGO会

Go语言高级编程_2.5 内部机制

2.5内部机制 对于刚刚接触CGO用户来说,CGO的很多特性类似魔法。CGO特性主要是通过一个叫cgo的命令行工具来辅助输出Go和C之间的桥接代码。本节我们尝试从生成的代码分析Go语言和C语言函数直接

Go语言高级编程_2.6 实战: 封装qsort

2.6实战:封装qsort qsort快速排序函数是C语言的高阶函数,支持用于自定义排序比较函数,可以对任意类型的数组进行排序。本节我们尝试基于C语言的qsort函数封装一个Go语言版本的qsort函

Go语言高级编程_2.8 C++ 类包装

2.8C++类包装 CGO是C语言和Go语言之间的桥梁,原则上无法直接支持C++的类。CGO不支持C++语法的根本原因是C++至今为止还没有一个二进制接口规范(ABI)。一个C++类的构造函数在编译为