A second to understand go to write the code of command-line tools

Time:2022-5-5

preface

Recently, because the project needs to be written for a period of timeGo, relative toJavaThe grammar is simple and at the same time has some advantagesPythonGrammar candy like that makes people shout “really fragrant”.

But at this stage, it is still relatively stablePythonWrite more. Sometimes I have to write backJava; Natural pairGoNot much familiar.

So it is convenient to do a small project on weekends to deepen some use experience. So I thought of using it beforeJavaWrite a blogGadgets

At that time, a large number of pictures in the microblog picture bed were prohibited from outside the chain, resulting in many pictures in personal blogs can not be viewed. This tool can back up the pictures in the article to the local, and can also replace the pictures directly to other picture beds.

Personally, I have been using it all the time, usually in codewordiPicSuch tools upload pictures to the microblog drawing bed (mainly convenient + free). After writing, use this tool to switch to[SM.MS](http://sm.MS)This kind of paid drawing bed will also back up the pictures to local disk.

Use insteadGoRewrite ascliAfter using the tool, the effect is as follows:

What skills do you need to master

The reason for choosing this toolGoTo rewrite; One is that the function is relatively simple, but it can also be usedGoSome features of, such as network IO, CO process synchronization and so on.

At the same time, it feels more geek after being modified to a command-line tool.

Before you start again, you are still unfamiliarGoofJavaerWhat knowledge points will be used:

  • Use and manage third-party dependency packages(go mod)
  • Application of collaborative process.
  • Multi platform packaging.

Let’s start the specific operation. I think even if I haven’t had much contact with itGoAfter reading, my friends can also quickly start to implement a gadget.

Use and manage third-party dependencies

  • If you haven’t installed go yet, please refer to the official website to install it yourself.

First, let’s introduce the dependency management of go1.11After that, the official comes with the dependency management module, so it is in the latest version1.15Has been strongly recommended in.

Its purpose and function andJavaMediummavenPythonMediumpipSimilar, but easier to usemavenMuch simpler.

According to its use reference, it needs to be executed under the project directory firstgo mod initUsed to initialize ago.modFiles, of course, if you useGoLangIn this wayIDE, when creating a new project, it will automatically help us create a directory structure. Of course, it also includesgo.modThis file.

In this document, we introduce the third-party package we need:


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
)

I use three packages here:

  • pb: progress bar, which is used to output the progress bar on the console.
  • color: used to output text in different colors on the console.
  • cli: command line tool development kit.

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)
	}
}

The code is very simple, just use itcliThe provided API creates several commands that will be entered by the user-dp-mpParameter mapping todownloadPathmarkdownPathVariable.

After that, it is convenient to scan all the pictures with these two data and download the pictures to the corresponding directory.

More user guides can be referred to directlyOfficial documents

You can see some syntax andJavaCompletely different, such as:

  • When declaring a variable, the type is put in the back, and the variable name is defined first; Method parameters are similar.
  • Type derivation, variable type can not be specified (new version)JavaThe () method supports returning multiple values at the same time, which is very easy to use.
  • Public and private functions are distinguished by the case of the initial letter.
  • There are others not listed one by one.

Synergetic process

Then the command execution place calledservice.DownLoadPic(markdownPath, downloadPath)Process business logic.

The codes such as file scanning and image downloading included here will not be analyzed; officialSDKIt’s very clear and simple.

Focus onGoInsidegoroutimeThat is, collaborative process.

The scenario I use here is to use a collaborative process to analyze and download images every time a file is scanned, so as to improve the overall operation efficiency.


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)
	}
}

In terms of code use, does it look better thanJavaIt’s much simpler. We don’t have to be likeJavaThat requires maintaining aexecutorService, there is no need to consider the size of the thread pool, and everything is left to youGoSchedule yourself.

When using, you only need to add before calling the functiongoKeyword, but here is an anonymous function.

And becausegoroutimeVery lightweight, andJavaMediumthreadIt takes up very little memory, so we don’t need to accurately control the number of creation.

But there is also a andJavaSomething very similar:WaitGroup

Its usage and function are similar toJavaMediumCountDownLatchVery similar; Mainly used to wait for allgoroutimeAfter execution, it is natural to wait for all the pictures to be downloaded, and then exit the program.

There are three main steps to use:

  • Create and initializegoruntimeQuantity of:wg.Add(len(number)
  • Whenever onegoruntimeCall after executionwg.Done()Subtract one from the count.
  • Final callwg.Wait()wait forWaitGroupThe number of is reduced to 0.

For the collaborative process, go is recommendedchanelTo communicate with each other, which will be discussed in the future.

pack

There are so many core logics. Let’s talk about packaging and operation; This andJavaThe difference is quite big.

as everyone knows,JavaThere is a famous saying:write once run anywhere

This is because there isJVMVirtual machine, so no matter which platform the code runs on, we only need to print one package; butGoHow can it run on different platforms without virtual machines.

In shortGoDifferent binary files can be packaged for different platforms. This file contains all the dependencies required for operation, and it does not even need to be installed on the target platformGoenvironment

  • Although Java only needs to pack one package in the end, it has to install compatible packages on various platformsJavaOperating environment.

I wrote one hereMakefileTo perform packaging: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)

You can see that we just need togo buildSpecify the system variable before to print packages of different platforms. For example, weLinuxSystematicarm64Schema package file:

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

It can be executed directly on the target platform./btbRun the program.

summary

All the codes in this article have been uploadedGithub: https://github.com/crossoverJie/btb

Those interested can also run the installation script directly.


curl -fsSL https://raw.githubusercontent.com/crossoverJie/btb/master/install.sh | bash

At present, this version only realizes the download and backup of pictures, and the replacement of drawing bed and other functions will be improved in the future.

Contact during this timeGoAfter the age of 25, it gives me a deep feelingJavaFor example,GoIt’s really a daunting thing to be born later. What’s more annoying is that it can’t be provoked even if it catches up with the wave of cloud primary.

Some minor problems that were not so important in the past have also been highlighted, such as slow startup, large memory occupation, verbose syntax, etc; However, I still look forward to the grandmaster who enjoys food, from the new versionJavaIt can be seen that it is also actively changing, not to mention its huge ecosystem that has not been shaken.

This is the end of this article about understanding the code of go writing command-line tools in one second. For more information about go writing command-line tools, please search the previous articles of developeppaer or continue to browse the relevant articles below. I hope you will support developeppaer in the future!