CLI:使用Go開發命令行應用

 

原文地址git

CLI或者「command line interface」是用戶在命令行下交互的程序。因爲經過將程序編譯到一個靜態文件中來減小依賴,一次Go特別適合開發CLI程序。若是你編寫過安裝時須要各類依賴的CLI程序你就知道這個是有多重要了。
   在這篇博客中咱們將介紹使用Go開發CLI的基本知識。 github

Arguments

   大多數CLI程序都須要輸入一些參數。Go 語言將這些參數以字符串slice處理。 golang

var Args []string

查找當前應用的名字。學習

package main

import (
    "fmt"
    "os"
)

func main() {
    // Program Name is always the first (implicit) argument
    cmd := os.Args[0]

    fmt.Printf("Program Name: %s\n", cmd)
}

這個應用再code/example1下,你能夠用一下命令編譯運行:ui

go build
./example1

輸出的結果是:spa

Program Name: ./example1
判斷傳入程序的參數數量

   爲了肯定有多少參數傳入,能夠計算全部參數的長度減1(記住,第一個參數老是程序的名字)。或者能夠直接從os.Args[1:]來判斷他的長度。 命令行

package main

import (
    "fmt"
    "os"
)

func main() {
    argCount := len(os.Args[1:])
    fmt.Printf("Total Arguments (excluding program name): %d\n", argCount)
}

運行./example2 獲得的結果將是0。運行./example2 -foo=bar 獲得的記過將是1。 code

遍歷參數

   下面是一個很快速的遍歷參數的例子。 blog

package main

import (
    "fmt"
    "os"
)

func main() {
    for i, a := range os.Args[1:] {
        fmt.Printf("Argument %d is %s\n", i+1, a)
    }

}
Running the program with ./example3 -local u=admin --help results in:
Argument 1 is -local
Argument 2 is u=admin
Argument 3 is --help
Flag 包

   目前爲止咱們已經知道如何在一個程序中查找參數的基本的方法。在這個級別查詢他們而且將他們賦值給咱們的程序是很麻煩的。全部就有了Flag包。 ip

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    flag.IntVar(&port, "p", 8000, "specify port to use.  defaults to 8000.")
    flag.Parse()

    fmt.Printf("port = %d", port)
}

咱們首先作的是設置一個int類型的默認值是8000,而且有文字提示的標識。
   爲了讓flag包對設置的變量賦值,須要是用flag.Parse()方法。
   不加參數的運行這個程序獲得的結果是port = 8000,由於咱們明確的指定了若是沒有參數傳遞給port,那麼就採用默認的8000.
   運行./example4 -p=9000 結果是 port = 9000
   同事flag提供了 「program useage」的輸出。若是咱們運行 ./example4 -help 咱們會獲得:

Usage of ./example4:
-p=8000: specify port to use.  defaults to 8000.
flag.Args()

   不少CLI程序同時包含有標識和沒有標識的參數。flag.Args() 將會直接返回哪些沒有標識的參數。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    flag.IntVar(&port, "p", 8000, "specify port to use.  defaults to 8000.")
    flag.Parse()

    fmt.Printf("port = %d\n", port)
    fmt.Printf("other args: %+v\n", flag.Args())
}
運行./example5 -p=9000 foo=10 -bar 將會獲得:
port = 9000
other args: [foo=10 -bar]

flag只要找到一個不包含的flag就會當即中止查詢。

無效的flag參數

   Go是一個強語言類型,因此若是咱們傳遞一個string給一個int類型的flag,它將會提示咱們:

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    flag.IntVar(&port, "p", 8000, "specify port to use.  defaults to 8000")
    flag.Parse()

    fmt.Printf("port = %d", port)
}
運行程序./example6 -p=foo 獲得的結果是:
invalid value 「foo」 for flag -p: strconv.ParseInt: parsing 「foo」: invalid syntax
Usage of ./example6:
-p=8000: specify port to use.  defaults to 8000

flag不只會提示咱們輸入錯誤,同時還會輸出默認的使用方法。

flag.Usage

   flag包聲明瞭一個Usage的方法。這樣咱們就能夠輸出咱們想要輸出的Usage了。

package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {
    flag.Usage = func() {
        fmt.Printf("Usage of %s:\n", os.Args[0])
        fmt.Printf("    example7 file1 file2 ...\n")
        flag.PrintDefaults()
    }
    flag.Parse()
}
運行./example7 –help 獲得的結果是:
Usage of ./example7:
example7 file1 file2 …
獲取輸入

   目前爲止咱們只是經過CLI輸出了信息,可是不接受任何輸入。咱們能夠基本的fmt.Scanf()來捕捉輸入。

package main

import "fmt"

func main() {
    var guessColor string
    const favColor = "blue"
    for {
        fmt.Println("Guess my favorite color:")
        if _, err := fmt.Scanf("%s", &guessColor); err != nil {
            fmt.Printf("%s\n", err)
            return
        }
        if favColor == guessColor {
            fmt.Printf("%q is my favorite color!", favColor)
            return
        }
        fmt.Printf("Sorry, %q is not my favorite color.  Guess again.\n", guessColor)
    }
}
bufio.Scanner

   fmt.Scanf 對於簡單的輸入頗有效,可是有時候咱們可能須要一整行的數據。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()
        if line == "exit" {
            os.Exit(0)
        }
        fmt.Println(line) // Println will add back the final '\n'
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
    }
}

這是一個基本的echo程序,若是要退出直接輸入exit便可。

一個基本的cat程序

   你應該用過不少次cat程序了。咱們將會把這篇博客學到的只是融合在一塊兒構建一個基本的cat程序。

package main

import (
    "flag"
    "fmt"
    "io"
    "os"
)

func main() {
    flag.Usage = func() {
        fmt.Printf("Usage of %s:\n", os.Args[0])
        fmt.Printf("    cat file1 file2 ...\n")
        flag.PrintDefaults()
    }

    flag.Parse()
    if flag.NArg() == 0 {
        flag.Usage()
        os.Exit(1)
    }

    for _, fn := range flag.Args() {
        f, err := os.Open(fn); 
        if err != nil {
            panic(err)
        }
        _, err = io.Copy(os.Stdout, f)
        if err != nil {
            panic(err)
        }
    }
}
幫助

   對於幫助咱們在上面已經講了,可是尚未明確的定義
-h
–help

   上面這些都會觸發help。

總結

  本篇博客中只是講了一些CLI的基本用法。若是想要學習更多,能夠查看這些包的godoc

其餘的命令行庫

   還有一些第三方庫可讓寫CLI程序更簡單:

相關文章
相關標籤/搜索