偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一個(gè)庫(kù),用Go搞定命令行程序

開(kāi)發(fā) 前端
Go 官方有一個(gè) flags 庫(kù)提供了最基礎(chǔ)的命令行參數(shù)支持,不過(guò)確實(shí)不好用,今天帶你認(rèn)識(shí)一個(gè)超贊的庫(kù)——urfave/cli,它能讓你用一種簡(jiǎn)單優(yōu)雅的方式來(lái)構(gòu)建命令行程序。

我們平時(shí)做的Go項(xiàng)目除了寫的各種API接口外,還經(jīng)常會(huì)寫任務(wù)腳本、命令行程序、定時(shí)任務(wù)等,其實(shí)這幾個(gè)是一個(gè)東西,你寫的任務(wù)腳本支持接受指令傳參,那它不就是命令行程序了?再把程序部署到服務(wù)器用Go Cron加個(gè)任務(wù)就是定時(shí)任務(wù)了。

圖片圖片

Go 官方有一個(gè) flags 庫(kù)提供了最基礎(chǔ)的命令行參數(shù)支持,不過(guò)確實(shí)不好用,今天帶你認(rèn)識(shí)一個(gè)超贊的庫(kù)——urfave/cli,它能讓你用一種簡(jiǎn)單優(yōu)雅的方式來(lái)構(gòu)建命令行程序。

什么是urfave/cli?

urfave/cli 是一個(gè)用 Go 編寫的、簡(jiǎn)單、快速且有趣的庫(kù),用于構(gòu)建命令行應(yīng)用程序。無(wú)論是小工具還是復(fù)雜的大型 CLI 程序,它都能輕松應(yīng)對(duì)。它的設(shè)計(jì)哲學(xué)是讓我們用聲明式的方式來(lái)定義命令、子命令、標(biāo)志(Flags),然后它會(huì)自動(dòng)幫你處理參數(shù)解析、幫助文檔生成等所有繁瑣的工作,聽(tīng)起來(lái)是不是很棒?

安裝

運(yùn)行以下命令來(lái)安裝 urfave/cli 的 v2 版本:

go get github.com/urfave/cli/v2

第一個(gè) CLI 程序

我們從經(jīng)典的 "Hello, World!" 開(kāi)始,創(chuàng)建一個(gè) main.go 文件,然后敲入以下代碼:

package main

import (
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Name:  "greet",
        Usage: "向世界打個(gè)招呼!",
        Action: func(c *cli.Context) error {
            println("Hello, world!")
            returnnil
        },
    }

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

運(yùn)行命令程序

go run main.go

你會(huì)看到終端輸出了 Hello, world!,當(dāng)然我們也可以 build 后用真正的命令去運(yùn)行

# build
go build -o greet ./main.go 
# 運(yùn)行命令
./greet

urfave/cli 自動(dòng)為我們生成了幫助信息。上面這個(gè)命令運(yùn)行時(shí)添加 --help 就能在控制臺(tái)輸出幫助信息。

添加命令行傳參

只會(huì)說(shuō) "Hello, world!" 可不夠,我們希望它能跟指定的人打招呼。這就要用到“標(biāo)志”(Flags)了。

我們來(lái)修改一下代碼,添加一個(gè) --name的標(biāo)志:

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Name:  "greet",
        Usage: "向世界或某人打個(gè)招呼!",
        Flags: []cli.Flag{
            &cli.StringFlag{
                Name:    "name",
                Value:   "world", // 默認(rèn)值
                Usage:   "指定打招呼的對(duì)象",
                Aliases: []string{"n"}, // 別名,-n 等同于 --name
            },
        },
        Action: func(c *cli.Context) error {
            name := c.String("name")
            fmt.Printf("Hello, %s!\n", name)
            returnnil
        },
    }

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

現(xiàn)在重新打包構(gòu)建一下這個(gè)命令

$ go build -o greet ./main.go
# 不帶任何參數(shù),使用默認(rèn)值
$ ./greet
Hello, world!

# 使用 --name 標(biāo)志
$ ./greet --name Gopher
Hello, Gopher!

# 使用別名 -n
$ ./greet -n 狗蛋
Hello, 狗蛋!

命令和子命令

當(dāng)你的工具功能越來(lái)越復(fù)雜時(shí),就需要引入“命令” 和 “子命令”來(lái)組織功能了。這就像 git 有 commit、push、pull 等子命令一樣。我們來(lái)模擬一個(gè)簡(jiǎn)單的文件處理工具 filetool。

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Name:  "filetool",
        Usage: "一個(gè)簡(jiǎn)單的文件處理工具",
        Commands: []*cli.Command{
            {
                Name:    "hash",
                Aliases: []string{"h"},
                Usage:   "計(jì)算文件的哈希值",
                Flags: []cli.Flag{
                    &cli.StringFlag{
                        Name:     "file",
                        Aliases:  []string{"f"},
                        Usage:    "指定輸入文件",
                        Required: true, // 這是一個(gè)必填項(xiàng)!
                    },
                },
                Action: func(c *cli.Context) error {
                    filePath := c.String("file")
                    // 這里的 hashFile 是我們自己實(shí)現(xiàn)的邏輯函數(shù)
                    fmt.Printf("正在為文件 '%s' 計(jì)算哈希...\n", filePath)
                    returnnil
                },
            },
        },
    }

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

上面是添加了命令,對(duì)于復(fù)雜的命令行程序,尤其是在業(yè)務(wù)系統(tǒng)里用作處理數(shù)據(jù)的命令行程序,往往還需要子命令的支持。這樣我們可以把處理一個(gè)大類數(shù)據(jù)的任務(wù)都劃分到同一個(gè)命令下,每個(gè)細(xì)分任務(wù)在寫成命令的子命令。

下面是一個(gè)添加子命令的簡(jiǎn)單例子:

var Word = &cli.Command{
 Name:    "word",
 Aliases: []string{"w"},
 Usage:   "Word文檔處理相關(guān)命令",
 Subcommands: []*cli.Command{
  {
   Name:  "parse",
   Usage: "解析Word文檔",
   Flags: []cli.Flag{
    &cli.StringFlag{
     Name:     "input",
     Aliases:  []string{"i"},
     Usage:    "輸入文件路徑",
     Required: true,
    },
    &cli.StringFlag{
     Name:     "output",
     Aliases:  []string{"o"},
     Usage:    "輸出文件路徑",
     Required: true,
    },
   },
   Action: func(c *cli.Context) error {
    return logic.NewWordLogic(c.Context).ParseWord(c.String("input"), c.String("output"))
   },
  },
 },
}

我們把這個(gè)子命令加到上面的

func main() {
    app := &cli.App{
        Name:  "filetool",
        Usage: "一個(gè)簡(jiǎn)單的文件處理工具",
        Commands: []*cli.Command{
            // ......
            word,
            // 添加更多命令
        },
    }
    // ......
}

上面這個(gè)子命令的調(diào)用方式如下:

$ go build -o filetool ./main.go;
./filetool word parse -i input.docx -o output.txt

最佳實(shí)踐

基礎(chǔ)用法已經(jīng)掌握了,但要構(gòu)建一個(gè)健壯、可維護(hù)的命令行工具,我們還需要借鑒一些真實(shí)項(xiàng)目中的經(jīng)驗(yàn)。下面這些技巧,能讓你的代碼質(zhì)量提升一個(gè)臺(tái)階。

鉤子函數(shù):用 Before和 After統(tǒng)一處理邏輯

你可能希望在每個(gè)命令執(zhí)行前后都做一些固定的操作,比如初始化日志、設(shè)置鏈路追蹤、上報(bào)監(jiān)控?cái)?shù)據(jù)或者記錄執(zhí)行時(shí)間等。urfave/cli 提供了 Before 和 After 鉤子函數(shù),來(lái)解決這個(gè)問(wèn)題。

下面是我的專欄項(xiàng)目使用 urfave/cli 時(shí)添加的鉤子:

func main() {
 app := &cli.App{
  Name:  "gm-tools",
  Usage: "Go Mall 工具集",
  Before: func(c *cli.Context) error {
   // 為每個(gè)命令創(chuàng)建帶有追蹤信息的上下文
   ctx := context.Background()
   spanId := util.GenerateSpanID(util.GetLocalIP())
   ctx = context.WithValue(ctx, "spanid", spanId)
   c.Context = ctx

   cmdName := strings.Join(c.Args().Slice(), " ")
   logger.Info(ctx, fmt.Sprintf("定時(shí)任務(wù)【%s】開(kāi)始執(zhí)行. 時(shí)間=【%s】)", cmdName, time.Now().Format(enum.TimeFormatHyphenedYMDHIS)))
   returnnil
  },
  After: func(c *cli.Context) error {
   // 記錄執(zhí)行的錯(cuò)誤
   if c.Context.Err() != nil {
    logger.Error(c.Context, "定時(shí)任務(wù)執(zhí)行失敗", c.Context.Err())
   }
   cmdName := strings.Join(c.Args().Slice(), " ")
   logger.Info(c.Context, fmt.Sprintf("定時(shí)任務(wù)【%s】執(zhí)行完成. 時(shí)間=【%s", cmdName, time.Now().Format(enum.TimeFormatHyphenedYMDHIS)))
   returnnil
  },
  Commands: []*cli.Command{
   commands.Word,
   // 添加更多工具命令
  },
 }

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

這樣無(wú)論你運(yùn)行哪個(gè)命令,Before 和 After 里的日志都會(huì)被打印出來(lái)。更重要的是,我們將一個(gè)帶有追蹤信息的 Go context.Context 注入到了 cli.Context 中,在后續(xù)的 Action 函數(shù)里,我們可以通過(guò) c.Context 取出這個(gè)上下文,并把它傳遞給業(yè)務(wù)邏輯,實(shí)現(xiàn)了全鏈路的追蹤!

責(zé)任編輯:武曉燕 來(lái)源: 網(wǎng)管叨bi叨
相關(guān)推薦

2023-03-31 08:44:55

Go開(kāi)發(fā)命令

2016-03-28 10:00:09

Swift命令程序

2022-06-07 07:21:19

Python內(nèi)置庫(kù)命令行

2019-04-16 06:50:34

2010-07-15 10:58:23

Perl命令行程序

2020-02-13 10:57:59

Python數(shù)據(jù)設(shè)計(jì)

2020-12-08 08:46:07

GoJava工具

2015-07-15 10:32:44

Node.js命令行程序

2022-09-27 13:07:41

clickPython命令行

2022-09-23 09:50:45

Python

2011-06-17 16:49:05

Cocoa蘋果

2018-06-12 15:10:11

Linuxvim命令PacVim

2023-10-30 01:00:42

Go語(yǔ)言Cobra庫(kù)

2016-08-10 12:41:00

Linux工具bcShell

2018-11-21 09:57:44

命令行Linux文件

2019-06-10 15:00:27

node命令行前端

2017-05-27 14:45:04

Linux命令進(jìn)程

2019-05-30 10:40:04

ddgrLinuxDuckDuckGo

2020-05-06 11:00:11

git命令GitHub

2022-02-17 18:21:47

工具HTTPie客戶端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)