Golang:命令行参数
#go #cli #100daysofgolang

介绍

在该系列的第25篇文章中,我们将研究Golang的命令行参数的解析。我们将探索如何进行解析的基础和使用程序中命令行中的位置参数或参数。通过使用osflag之类的标准库包,我们可以制作功能强大但易于构建的CLI应用程序和程序。

从命令行(OS软件包)解析参数

我们可以使用OS软件包从GO脚本中的命令行获取参数。我们必须在OS软件包中使用ARGS变量。 Args变量是字符串片,因此是命令行中解析的参数。

  • 第一个(0索引)参数是程序的途径

  • 第一个索引开始是通过的实际参数。

package main

import (
    "fmt"
    "os"
)

func main() {
    args := os.Args
    fmt.Printf("Type of Args = %T\n", args)
    fmt.Println(args[0], args[1])
}
$ go run main.go hello
Type of Args = []string
/tmp/go-build1414795487/b001/exe/main hello

在上面的示例中,我们可以看到Args是字符串片,我们可以按照从命令行传递的参数获得索引。

如果您不解析任何参数并访问第一个参数作为os.Args[1],则会导致index out of range错误。因此,您需要首先检查该参数是否已解析并设置默认值。

package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() {
    var port int
    var err error
    if len(os.Args) > 1 {
        port, err = strconv.Atoi(args[1])
        if err != nil {
            panic(err)
        }
    } else {
        port = 8000
    }
    fmt.Println(port)
}
$ go run main.go
8000

$ go run main.go 7000
7090

$ go run main.go h
panic: strconv.Atoi: parsing "h": invalid syntax

在上面的示例中,我们已将端口变量声明为整数,并试图查看我们是否使用LEN函数从命令行中解析了一个参数使用strconv.Atoi函数。如果该过程中有任何错误,我们将记录一个错误消息,并在程序中恐慌。因此,这就是我们可以设置默认值或从Golang中的命令行中检查任何参数的方法。

获取args的数量

我们可以将LEN函数与Args slice一起使用,以获取命令行的参数总数。为了忽略第一个参数,这将是该程序的途径,我们可以将第一个元素切成os.Args[1:]。这将切成第一个索引的参数列表,直到切片中的最后一个元素。

package main

import (
    "fmt"
    "os"
)

func main() {
    total_args := len(os.Args[1:])
    fmt.Println("Total Args =", total_args)
}
$ go run main.go hello world 56

Total Args = 3

这只会为我们提供从命令行传递的参数数量,不包括第一个(0)参数,该参数是当前程序的执行路径的默认参数。

在所有论点上迭代

我们可以使用simple for循环,范围在os.Argsos.Args[1:]上进行迭代,以迭代从命令行传递的每个参数。

package main

import (
    "fmt"
    "os"
)

func main() {
    for n, args := range os.Args {
        fmt.Println("Arg", n, "->", args)
    }

    /* 
    // For excluding the 0th argument
    for n, args := range os.Args[1:] {
        fmt.Println("Arg", n, "->", args)
    }
    */
}
$ go run main.go hello world 56
Arg 0 -> /tmp/go-build2248205073/b001/exe/main
Arg 1 -> hello
Arg 2 -> world
Arg 3 -> 56

现在,我们可以使用简单的循环从命令行中迭代从命令行传递的参数。我们可以根据程序的要求和需求进一步处理这些论点。

使用标志软件包

golang在其标准库中有一个包装,称为flags,它使我们能够从命令行中解析具有许多内置功能的命令线和参数。例如,默认值很容易通过简单的函数参数解析,如果在解析参数或标志的错误,自定义和自由的自定义和自由的情况下,可以为参数类型选择数据类型,等等。对于裸露和快速CLI程序,标志包是一个不错的选择。

解析标志

我们可以使用flags包中提供的函数(如IntVar)用于整数值,StringVar for String,BoolVar用于布尔值等。每个函数都采用4个参数,它们从命令行解析的参数/标志设置了解析变量的值。

  • 第一个参数是对存储值的变量的引用。

  • 第二个参数是要从命令行读取的参数/标志的名称。

  • 第三个参数是变量的默认值。

  • 第四参数是该参数/标志的帮助文本。

因此,让我们以命令行解析端口号解析的前一个示例。我们可以使用flag.IntVar(&port, "p", 8000, "Provide a port number"),这将从命令行设置变量端口的值,为-p 6789的值或默认值为8000。如果用户提供了非直集或无效的值作为错误消息,将使用帮助文本。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    var dir string
    var publish bool

    flag.IntVar(&port, "p", 8000, "Provide a port number")
    flag.StringVar(&dir, "dir", "output_dir", "Directory")
    flag.BoolVar(&publish, "publish", false, "Publish the article")

    flag.Parse()

    fmt.Println(port)
    fmt.Println(dir)
    fmt.Println(publish)

    if publish {
        fmt.Println("Publishing article...")
    } else {
        fmt.Println("Article saved as Draft!")
    }
}
$ go run flag.go

8000
output_dir
false
Article saved as Draft!


$ go run flag.go -p 1234

1234
output_dir
false
Article saved as Draft!


$ go run flag.go -p 1234 -dir site_out

1234
site_out
false
Article saved as Draft!


$ go run flag.go -publish

8000
output_dir
true
Publishing article...

因此,在上面的示例中,我们使用了一些类型的值,例如portIntegerVarStringVardirBoolVar用于publish。如前所述,功能采用相同格式的4个参数,对具有分析值的变量的引用,参数/标志的名称,变量将保留的默认值以及帮助文本或用法字符串。 BoolVar略有不同,但在逻辑上效果很好,如果我们解析-publish,则值将以truefalse设置为否则。您可以手动添加像-publish true这样的值,依此类推,但这不是强制性的,也不理解为真。

在上面的示例中,我们在输出中解析了不同的参数,并显示了这些标志的值。如果我们不指定值,则可以看到正在解析的默认值,如果是bool变量,则将默认值视为false。因此,我们可以看到我们可以轻松地从Golang的命令行使用和解析标志,这很简单,快速且可扩展。

对于其他数据类型,标志软件包具有诸如float64值的Float64VarDurationVar的函数,用于时间持续时间值,而TextVar的其他类型的TextVar则由文本的删除。

推断出。

从脚本设置标志

我们可以使用标志软件包中的Set方法从脚本而不是从命令行中设置标志/参数的值。 Set方法将两个值作为参数的参数名称和该参数的值设置为。如果在参数设置期间出现任何错误,它会返回错误。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    var dir string
    var publish bool

    flag.IntVar(&port, "p", 8000, "Provide a port number")
    flag.StringVar(&dir, "dir", "output_dir", "Directory")

    flag.Parse()

    fmt.Println(port)
    fmt.Println(dir)
    flag.Set("dir", "dumps")
    fmt.Println(dir)
}
$ go run flag.go -p 8080
8080
output_dir
dumps

因此,可以清楚地看到参数的值可以在脚本中更改,也可以更改关联变量的值。请记住,我们将两参数作为字符串,因此第一个参数是参数的名称,而不一定是变量名称。

使用参数(指针)

此外,还有一些功能,例如IntFloat64StringBool在标志软件包中,可以允许在不使用Parse方法的情况下获取参数的值。我们使用存储在中的值的引用为参数,而不是将变量定义为数据值。我们有一个指向数据值的指针。

package main

import (
    "flag"
    "fmt"
)

func main() {
    port := flag.Int("p", 8000, "Provide a port number")
    dir := flag.String("dir", "output_dir", "Directory")
    publish := flag.Bool("publish", false, "Publish the article")
    help := flag.Bool("help", false, "Help")

    if *help {
        flag.PrintDefaults()
    } else {
        fmt.Println(*port)
        fmt.Println(*dir)
        flag.Set("dir", "dumps")
        fmt.Println(*dir)

        fmt.Println(flag.NFlag())
        fmt.Println(flag.NArg())

        fmt.Println(*publish)

        if *publish {
            fmt.Println("Publishing article...")
        } else {
            fmt.Println("Article saved as Draft!")
        }
        vals := flag.Args()
        fmt.Println(vals)
    }
}
$ go run flag.go -p 80 -dir node_mods 1234
80
node_mods
dumps
2
1
false
Article saved as Draft!
[1234]

我们可以执行相同的任务,但是我们必须使用指针作为参数的引用,而不是将它们存储在实际的内存地址中。我们已经对参数和标志进行了与其他示例相同的操作。

我们首先,使用Int方法或其他合适的方法,可以在一般用例中使用String,该函数返回参数/flag的实际存储值的参考(内存地址)。我们可以使用*操作员从其内存地址访问该值。在该系列的最后一部分中,我们涵盖了pointer算术。当我们使用*port时,我们从内存地址获得值,从而可以将其用于程序中的必需任务,我们还可以通过创建具有该参数值的新变量来存储该变量的副本。

解析论点

因此,如果我们想用单个值解析标志,我们已经看到了flag.Args函数的使用来获取从命令行传递的参数的值,该命令行传递了没有任何标记标签(只是来自CMD的原始参数)。就像我们使用os.Args变量一样,但此功能非常干净,并且过滤了程序参数的路径。因此,我们可以直接拥有用户从命令行明确传递的参数。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    flag.IntVar(&port, "p", 8000, "Provide a port number")
    flag.Parse()
    fmt.Println(port)
    vals := flag.Args()
    fmt.Println(vals)
}
$ go run flag.go -p 8123
8123
[]


$ go run flag.go -p 8123 1234 hello true
8123
[1234 hello true]


$ go run flag.go -p 8123 1234 hello true -p 9823 world
8123
[1234 hello true -p 9823 world]

在上面的示例中,我们可以看到我们使用了命令行中的一些非标记参数。 Args函数的返回值是字符串片,然后我们可以使用类型铸造和函数将其转换为适当的类型。解析标记的参数后,如果我们使用Args函数,则将不可能在命令行中再次使用标记的参数。此后将被视为一个简单的字符串。

就是这部分。所有代码示例和命令的参考可以在100 days of Golang GitHub存储库中找到。

用printDefaults获取帮助文本

我们可以使用flag.PrintDefaults方法仅打印脚本中命令行的预期参数的默认值和帮助文本。我们可以简单地将其用作帮助标志,也可以将其在错误消息中使用以引导用户到适当的参数和标志。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    var help bool
    flag.IntVar(&port, "p", 8000, "Provide a port number")
    flag.BoolVar(&help, "help", false, "Help")
    flag.Parse()
    if help {
        flag.PrintDefaults()
    } else {
        fmt.Println(port)
        vals := flag.Args()
        fmt.Println(vals)
    }
}
$ go run help.go -h

Usage of /tmp/go-build121267600/b001/exe/help:
  -help
        Help
  -p int
        Provide a port number (default 8000)


$ go run help.go

8000
[]

因此,我们可以看到PrintDefaults函数将简单地打印脚本中预期的标志和这些标志的默认值的辅助文本。这可用于为简单终端应用程序提供良好的用户友好界面。

获取参数数量

我们可以在flag软件包中使用NFlag方法。该函数返回一个整数,该整数指示从命令行设置的参数计数。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    var dir string
    var publish bool

    flag.IntVar(&port, "p", 8000, "Provide a port number")
    flag.StringVar(&dir, "dir", "output_dir", "Directory")

    flag.Parse()

    fmt.Println(port)
    fmt.Println(dir)
    fmt.Println(flag.NFlag())
}
$ go run flag.go
8000
output_dir
0


$ go run flag.go -p 8080 8999 false hello
8080
output_dir
1


$ go run flag.go -p 8080 -dir dumps hello 1234
8080
dumps
2

port标志已从命令行设置,因此我们只有一个参数集,因此函数NFlag返回1作为集旗的数量。

另外,NArg方法还将返回一个整数,该整数将计算已提供的参数数量。

package main

import (
    "flag"
    "fmt"
)

func main() {
    var port int
    var dir string
    var publish bool

    flag.IntVar(&port, "p", 8000, "Provide a port number")
    flag.StringVar(&dir, "dir", "output_dir", "Directory")

    flag.Parse()

    fmt.Println(port)
    fmt.Println(dir)
    fmt.Println(flag.NArg())
}
$ go run flag.go 1234
8000
output_dir
1


$ go run flag.go -p 8080 -dir dumps hello 1234
8080
dumps
2


$ go run flag.go -p 8080 hello 1234 false
8080
dumps
3

在第一个示例中,我们没有任何标志参数设置,我们只有一个未构造的参数为1234,因此NArg函数返回1。第二个示例具有2个未标记的值,我们将portdir的值分别设置为8080dumps,因此其余的未封闭值为hello1234,因此返回值为2。第三个示例具有3个非网络值,因此我们返回3

就是这部分。所有代码示例和命令的参考可以在100 days of Golang GitHub存储库中找到。

结论

我们已经看到如何使用osflag软件包在Golang中解析命令行参数。尽管这两个不是构建CLI应用程序的唯一选择,但它们提供了一种干净易于启动的方法,但它们还具有标准库,这使它变得更好,因为我们不必与第三方库混合在一起。我们从命令行计划中看到了解析标志和参数的基础。

感谢您的阅读。如果您有任何疑问,问题或反馈,可以在下面的讨论中或我的社交手柄上告诉我。快乐编码:)系列:“ [''100天Golang']