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程序更简单:

相关文章
相关标签/搜索