Goを使用して1時間でコマンドラインツールを作成する方法を学ぶ

Goを使用して1時間でコマンドラインツールを作成する方法を学ぶ
Foreword
Goは、このプロジェクトのために最近しばらくの間書かれています。Javaと比較すると、構文は単純で、Pythonのような構文上の砂糖が含まれているため、人々は「本当に香りがよい」と叫びます。

Goを使用して1時間でコマンドラインツールを作成する方法を学ぶ
しかし、この段階では、比較的言えば、私はまだPythonでさらに多くのことを書いています。時には、戻ってJavaを書かなければならないこともあります。当然、Goについてはあまり知りません。

したがって、週末に小さなプロジェクトを作成して、経験を深めるのに便利です。そこで、以前はJavaで書かれたブログガジェットを考えました。

当時、Weibo Tubedの多数の写真はリンクが禁止されていたため、個人のブログの多くの写真を閲覧できなくなりました。このツールは、記事内の写真をローカルにバックアップしたり、写真を他の写真に直接置き換えることもできます。

Goを使用して1時間でコマンドラインツールを作成する方法を学ぶ

個人的にはずっと使っていて、コードワードを使うときはiPicなどのツールを使ってWeibo Tubed(主に便利+無料)に写真をアップロードしています。書き込み後、このツールを使用して、ワンクリックSM.MSなどの有料イメージベッドに切り替えると、イメージが同時にローカルディスクにバックアップされます。

Goを使用してcliツールとして書き換えた場合の効果は次のとおりです。

Goを使用して1時間でコマンドラインツールを作成する方法を学ぶ
3-min.gif

習得する必要のあるスキル
Goで書き直すためにこのツールを選んだ理由の1つは、機能が比較的単純であると同時に、ネットワークIO、定期的な同期など、Goのいくつかの特性を利用できることです。

同時に、コマンドラインツールに変更した後、よりオタクに感じますか?

始める前に、Goに慣れていないJavaerのためのいくつかの知識ポイントを紹介しましょう。

  • サードパーティの依存関係パッケージを使用および管理する(go mod)
  • コルチンの使用。
  • マルチプラットフォームパッケージ。
    具体的な操作から始めましょう。Goに触れたことがない友達でも、読んだらすぐに小さなツールの実装を始めることができると思います。

サードパーティの依存関係を使用および管理する

  • Goをまだインストールしていない場合は、公式Webサイトを参照して自分でインストールしてください。
    まず、Goの依存関係管理を紹介します。バージョン1.11以降、公式の依存関係管理モジュールが含まれているため、現在の最新バージョン1.15で強くお勧めします。

その目的と機能は、JavaのmavenやPythonのpipに似ていますが、mavenよりもはるかに簡単に使用できます。

Goを使用して1時間でコマンドラインツールを作成する方法を学ぶ
使用リファレンスによると、go.modファイルを初期化するにはプロジェクトディレクトリでgo mod initを実行する必要があります。もちろん、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
)

ここでは、次の3つのパッケージを使用しました。

  • pb:進行状況バー。コンソールに進行状況バーを出力するために使用されます。
  • 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 {
フラグ:[] cli.Flag {
&cli.StringFlag {
名前: "モデル"、
使用法: "操作モード; r:置換、b:バックアップ"、デフォルトテキスト
: "b"、
エイリアス:[] string {"m"}、
必須:true、
宛先:&model、
}、
&cli.StringFlag {
名前: "download-path"、
使用法: "画像が保存されるパス"、
エイリアス:[] string {"dp" }、
宛先:&downloadPath、
必須:true、
値:constants.DownloadPath、
}、
&cli.StringFlag {
名前: " markdown -path"、
使用法: "マークダウンファイルが保存されているパス"、
エイリアス:[] string {" mp "}、
宛先:&markdownPath、
必須:true、
値:constants.MarkdownPath、
}、
}、
アクション:func(c * cli.Context)エラー{
service.DownLoadPic(markdownPath 、downloadPath)

return nil
}、
名前: "btb"、
使用法: "ブログの画像のバックアップと置換を支援する"、
}

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 里的 goroutime 也就是协程。

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

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

if err!= nil {
log.Fatal( "ファイルの読み取りエラー")
}

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( "[%v]ファイルの処理に成功しました。\ n"、len(* allFile))

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


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

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

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

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

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

使用起来主要分为三步:

* 创建和初始化 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 = btb
GOBUILD = go build -ldflags "-s -w" -o $ {BINARY}
GOCLEAN = go clean
RMTARGZ = rm -rf * .gz
VERSION = 0.0.1

リリース:

掃除

$(GOCLEAN)
$(RMTARGZ)

Mac用にビルド

CGO_ENABLED = 0 GOOS = darwin GOARCH = amd64 $(GOBUILD)
tar czvf $ {BINARY} -mac64- $ {VERSION} .tar.gz ./${BINARY}

腕のために構築する

$(GOCLEAN)
CGO_ENABLED = 0 GOOS = linux GOARCH = arm64 $(GOBUILD)
tar czvf $ {BINARY} -arm64- $ {VERSION} .tar.gz ./${BINARY}

Linux用にビルド

$(GOCLEAN)
CGO_ENABLED = 0 GOOS = linux GOARCH = amd64 $(GOBUILD)
tar czvf $ {BINARY} -linux64- $ {VERSION} .tar.gz ./${BINARY}

勝利のために構築する

$(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 可以看出也在积极改变,更不用说它还有无人撼动的庞大生态。

おすすめ

転載: blog.51cto.com/15049794/2562890