CLI或者“command line interface”是用戶在命令行下交互的程序。由於通過將程序編譯到一個靜態文件中來減少依賴,一次Go特別適合開發CLI程序。如果你編寫過安裝時需要各種依賴的CLI程序你就知道這個是有多重要了。
在這篇博客中我們將介紹使用Go開發CLI的基本知識。
Arguments
大多數CLI程序都需要輸入一些參數。Go 語言將這些參數以字符串slice處理。
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下,你可以用一下命令編譯運行:
go build
./example1
輸出的結果是:
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。
遍歷參數
下面是一個很快速的遍歷參數的例子。
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包。
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()) }
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) }
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() }
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程序更簡單:
