您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

十分钟学会用 Go 编写命令行工具

时间:2021-11-25 09:17:33  来源:  作者:crossoverJie
十分钟学会用 Go 编写命令行工具

 

前言

最近因为项目需要写了一段时间的 Go ,相对于 JAVA 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。

十分钟学会用 Go 编写命令行工具

 

但现阶段相对来说还是 Python 写的多一些,偶尔还得回炉写点 Java ;自然对 Go 也谈不上多熟悉。

于是便利用周末时间自己做个小项目来加深一些使用经验。于是我便想到了之前利用 Java 写的一个博客小工具。

那段时间正值微博图床大量图片禁止外链,导致许多个人博客中的图片都不能查看。这个工具可以将文章中的图片备份到本地,还能将图片直接替换到其他图床。

十分钟学会用 Go 编写命令行工具

 

我个人现在是一直在使用,通常是在码字的时候利用 iPic 之类的工具将图片上传到微博图床(主要是方便+免费)。写完之后再通过这个工具一键切换到 [SM.MS](http://sm.MS) 这类付费图床,同时也会将图片备份到本地磁盘。

改为用 Go 重写为 cli 工具后使用效果如下:

十分钟学会用 Go 编写命令行工具

 

3-min.gif

需要掌握哪些技能

之所以选择这个工具用 Go 来重写;一个是功能比较简单,但也正好可以利用到 Go 的一些特点,比如网络 IO、协程同步之类。

同时修改为命令行工具后是不是感觉更极客了呢。

再开始之前还是先为不熟悉 Go 的 Javaer 介绍下大概会用到哪些知识点:

  • 使用和管理第三方依赖包(go mod)
  • 协程的运用。
  • 多平台打包。

下面开始具体操作,我觉得即便是没怎么接触过 Go 的朋友看完之后也能快速上手实现一个小工具。

使用和管理第三方依赖

  • 还没有安装 Go 的朋友请参考官网自行安装。

首先介绍一下 Go 的依赖管理,在版本 1.11 之后官方就自带了依赖管理模块,所以在当下最新版 1.15 中已经强烈推荐使用。

它的目的和作用与 Java 中的 maven,Python 中的 pip 类似,但使用起来比 maven 简单许多。

十分钟学会用 Go 编写命令行工具

 

根据它的使用参考,需要首先在项目目录下执行 go mod init 用于初始化一个 go.mod 文件,当然如果你使用的是 GoLang 这样的 IDE,在新建项目时会自动帮我们创建好目录结构,当然也包含 go.mod 这个文件。

在这个文件中我们引入我们需要的第三方包:

module btb

go 1.15

require (
 github.com/cheggaaa/pb/v3 v3.0.5
 github.com/fatih/color v1.10.0
 github.com/urfave/cli/v2 v2.3.0
)

我这里使用了三个包,分别是:

  • pb: progress bar,用于在控制台输出进度条。
  • color: 用于在控制台输出不同颜色的文本。
  • cli: 命令行工具开发包。

import (
 "btb/constants"
 "btb/service"
 "github.com/urfave/cli/v2"
 "log"
 "os"
)

func main() {
 var model string
 downloadPath := constants.DownloadPath
 markdownPath := constants.MarkdownPath

 App := &cli.App{
  Flags: []cli.Flag{
   &cli.StringFlag{
    Name:        "model",
    Usage:       "operating mode; r:replace, b:backup",
    DefaultText: "b",
    Aliases:     []string{"m"},
    Required:    true,
    Destination: &model,
   },
   &cli.StringFlag{
    Name:        "download-path",
    Usage:       "The path where the image is stored",
    Aliases:     []string{"dp"},
    Destination: &downloadPath,
    Required:    true,
    Value:       constants.DownloadPath,
   },
   &cli.StringFlag{
    Name:        "markdown-path",
    Usage:       "The path where the markdown file is stored",
    Aliases:     []string{"mp"},
    Destination: &markdownPath,
    Required:    true,
    Value:       constants.MarkdownPath,
   },
  },
  Action: func(c *cli.Context) error {
   service.DownLoadPic(markdownPath, downloadPath)

   return nil
  },
  Name:  "btb",
  Usage: "Help you backup and replace your blog's images",
 }

 err := app.Run(os.Args)
 if err != nil {
  log.Fatal(err)
 }
}

代码非常简单,无非就是使用了 cli 所提供的 api 创建了几个命令,将用户输入的 -dp、-mp 参数映射到 downloadPath、markdownPath 变量中。

之后便利用这两个数据扫描所有的图片,以及将图片下载到对应的目录中。

更多使用指南可以直接参考官方文档。

可以看到部分语法与 Java 完全不同,比如:

  • 申明变量时类型是放在后边,先定义变量名称;方法参数类似。
  • 类型推导,可以不指定变量类型(新版本的 Java 也支持)
  • 方法支持同时返回多个值,这点非常好用。
  • 公共、私用函数利用首字母大小写来区分。
  • 还有其他的就不一一列举了。

协程

紧接着命令执行处调用了 service.DownLoadPic(markdownPath, downloadPath) 处理业务逻辑。

这里包含的文件扫描、图片下载之类的代码就不分析了;官方 SDK 写的很清楚,也比较简单。

重点看看 Go 里的 goroutine 也就是协程。

我这里使用的场景是每扫描到一个文件就利用一个协程去解析和下载图片,从而可以提高整体的运行效率。

func DownLoadPic(markdownPath, downloadPath string) {
 wg := sync.WaitGroup{}
 allFile, err := util.GetAllFile(markdownPath)
 wg.Add(len(*allFile))

 if err != nil {
  log.Fatal("read file error")
 }

 for _, filePath := range *allFile {

  go func(filePath string) {
   allLine, err := util.ReadFileLine(filePath)
   if err != nil {
    log.Fatal(err)
   }
   availableImgs := util.MatchAvailableImg(allLine)
   bar := pb.ProgressBarTemplate(constants.PbTmpl).Start(len(*availableImgs))
   bar.Set("fileName", filePath).
    SetWidth(120)

   for _, url := range *availableImgs {
    if err != nil {
     log.Fatal(err)
    }
    err := util.DownloadFile(url, *genFullFileName(downloadPath, filePath, &url))
    if err != nil {
     log.Fatal(err)
    }
    bar.Increment()

   }
   bar.Finish()
   wg.Done()

  }(filePath)
 }
 wg.Wait()
 color.Green("Successful handling of [%v] files.n", len(*allFile))

 if err != nil {
  log.Fatal(err)
 }
}

就代码使用层面看起来是不是要比 Java 简洁许多,我们不用像 Java 那样需要维护一个 executorService,也不需要考虑这个线程池的大小,一切都交给 Go 自己去调度。

使用时只需要在调用函数之前加上 go 关键字,只不过这里是一个匿名函数。

而且由于 goroutine 非常轻量,与 Java 中的 thread 相比占用非常少的内存,所以我们也不需要精准的控制创建数量。


不过这里也用到了一个和 Java 非常类似的东西:WaitGroup。

它的用法与作用都与 Java 中的 CountDownLatch 非常相似;主要用于等待所有的 goroutine 执行完毕,在这里自然是等待所有的图片都下载完毕然后退出程序。

使用起来主要分为三步:

  • 创建和初始化 goruntime 的数量:wg.Add(len(number)
  • 每当一个 goruntime 执行完毕调用 wg.Done() 让计数减一。
  • 最终调用 wg.Wait() 等待WaitGroup 的数量减为0。

对于协程 Go 推荐使用 chanel 来互相通信,这点今后有机会再讨论。

打包

核心逻辑也就这么多,下面来讲讲打包与运行;这点和 Java 的区别就比较大了。

众所周知,Java 有一句名言:write once run anywhere

这是因为有了 JVM 虚拟机,所以我们不管代码最终运行于哪个平台都只需要打出一个包;但 Go 没有虚拟机它是怎么做到在个各平台运行呢。

简单来说 Go 可以针对不同平台打包出不同的二进制文件,这个文件包含了所有运行所需要的依赖,甚至都不需要在目标平台安装 Go 环境。

  • 虽说 Java 最终只需要打一个包,但也得在各个平台安装兼容的 Java 运行环境。

我在这里编写了一个 Makefile 用于执行打包:make release

# Binary name
BINARY=btb
GOBUILD=go build -ldflags "-s -w" -o ${BINARY}
GOCLEAN=go clean
RMTARGZ=rm -rf *.gz
VERSION=0.0.1

release:
 # Clean
 $(GOCLEAN)
 $(RMTARGZ)
 # Build for mac
 CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GOBUILD)
 tar czvf ${BINARY}-mac64-${VERSION}.tar.gz ./${BINARY}
 # Build for arm
 $(GOCLEAN)
 CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GOBUILD)
 tar czvf ${BINARY}-arm64-${VERSION}.tar.gz ./${BINARY}
 # Build for linux
 $(GOCLEAN)
 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD)
 tar czvf ${BINARY}-linux64-${VERSION}.tar.gz ./${BINARY}
 # Build for win
 $(GOCLEAN)
 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GOBUILD).exe
 tar czvf ${BINARY}-win64-${VERSION}.tar.gz ./${BINARY}.exe
 $(GOCLEAN)

可以看到我们只需要在 go build 之前指定系统变量即可打出不同平台的包,比如我们为 Linux 系统的 arm64 架构打包文件:

CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build main.go -o btb

便可以直接在目标平台执行 ./btb 运行程序。

总结

本文所有代码都已上传 Github: https://github.com/crossoverJie/btb

感兴趣的也可以直接运行安装脚本体验。

curl -fsSL https://raw.githubusercontent.com/crossoverJie/btb/master/install.sh | bash
  • 目前这个版本只实现了图片下载备份,后续会完善图床替换及其他功能。

这段时间接触 Go 之后给我的感触颇深,对于年纪 25 岁的 Java 来说,Go 确实是后生可畏,更气人的是还赶上了云原生这个浪潮,就更惹不起了。

一些以前看来不那么重要的小毛病也被重点放大,比如启动慢、占用内存多、语法啰嗦等;不过我依然对这位赏饭吃的祖师爷保持期待,从新版本的 Java 可以看出也在积极改变,更不用说它还有无人撼动的庞大生态。

更多 Java 后续内容可以参考周志明老师的文章:云原生时代,Java危矣?



Tags:Go   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  Tags: Go  点击:(29)  评论:(0)  加入收藏
在本教程中,我们将介绍如何使用 Django 发送电子邮件。我们将介绍如何配置 Django SMTP 连接,如何为您的电子邮件提供商设置应用程序密码,以及如何通过 Django shell 发送电子...【详细内容】
2021-11-10  Tags: Go  点击:(21)  评论:(0)  加入收藏
前言在 4 月 27 日举办的 Gopher China 2019 中,来自花椒直播的周洋进行了题为《花椒直播关于创业公司中台的技术思考与实践》的演讲,以下为演讲实录。No.0背景介绍 周洋:2009...【详细内容】
2021-06-29  Tags: Go  点击:(106)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  Tags: Go  点击:(125)  评论:(0)  加入收藏
django 编写数据接口django-admin•django-shell 新增文章太复杂•创建管理员用户•登陆页面进行管理创建超级用户python manage.py createsuperuser访问:http:/...【详细内容】
2021-06-16  Tags: Go  点击:(139)  评论:(0)  加入收藏
从用了近十年的 C# 转到 Go 是一个有趣的旅程。有时,我陶醉于 Go 的简洁[1];也有些时候,当熟悉的 OOP (面向对象编程)模式[2]无法在 Go 代码中使用的时候会感到沮丧。幸运的是,我...【详细内容】
2021-03-11  Tags: Go  点击:(285)  评论:(0)  加入收藏
本文作者:nopsky,投稿发布初衷市面上优秀的ORM已经很多了,例如gorm,xorm,sqlx等,已经足够满足我们日常使用的各种场景了。但对于像我这样喜欢简单好用的人来说gorm,xorm就像一个庞...【详细内容】
2021-03-03  Tags: Go  点击:(259)  评论:(0)  加入收藏
前言之前遇到过这样一个情况(发现问题的结构体并不长这样, 不过为了引出问题, 改了一下):type Test struct { b bool i3 int32 i8 int8 i64 int64 by byte}func main(...【详细内容】
2021-02-25  Tags: Go  点击:(221)  评论:(0)  加入收藏
本文选自“字节跳动基础架构实践”系列文章。 “字节跳动基础架构实践”系列文章是由字节跳动基础架构部门各技术团队及专家倾力打造的技术干货内容,和大家分享团队在基础架...【详细内容】
2021-01-18  Tags: Go  点击:(259)  评论:(0)  加入收藏
来学习下 Go 语言的安全检查工具 gosec。• 来源:linux.cn • 作者:Gaurav Kamathe • 译者:lxbowlf •(本文字数:11199,阅读时长大约:13 分钟) Go 语言 写的代码...【详细内容】
2020-12-22  Tags: Go  点击:(100)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(12)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(196)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(272)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条