您现在的位置: 首页 > 网站导航收录 > 百科知识百科知识
(lnt是什么意思)-lolnt是什么意思
命令,参数,命令行(lnt是什么意思)-lolnt是什么意思
发布时间:2020-12-06加入收藏来源:互联网点击:
另外,又基于这些定义延伸出更多的概念,比如 persistent flags代表适用于所有子命令的 flag,local flags 代表只用于当前子命令的 flag, required flags代表必选 flag 等等。
这些定义是 cobra 的核心设计来源,要想解决我上面提到的两个问题,我们需要重新审视这些定义。为此,我们从头开始一步步分析何为一个命令行。
1 命令行只是一个可被 shell 解析执行的字符串
$ cmd arg1 arg2 arg3命令行及其参数,本质上就是一个字符串而已。字符串的含义是由 shell来解释的,对于 shell来说,一个命令行由命令和参数组成,命令和参数以及参数和参数之间是由空白符分割。
还有别的吗? 没了,没有什么父命令、子命令,也没有什么持久参数、本地参数,一个参数是双横线(--) 、单横线(-)还是其他字符开头,都没有关系,这只是字符串而已,这些字符串由 shell 传递给你要执行的程序,并放到 os.Args (Go 语言)这个数组里。
2 参数、标识与选项
从上面的描述可知,参数(argument)是对命令行后面那一串空白符分隔的字符串的称呼,而一个参数,在命令行中又可以赋予不同的含义。
以横线或双横线开头的参数看起来有些特殊,结合代码来看,这种类型的参数有其独特的作用,就是将某个值跟代码中的某个变量关联起来,这种类型的参数,我们叫做标识(flag)。回想一下,os.Args 这个数组里的参数有很多,这些参数跟命令中的变量是没有直接关系的,而 flag 提供的本质上是一个键值对,我们的代码中,通过把键跟某个变量关联起来,从而实现了对这个变量赋值的功能。
flag.IntVar(&limit, "limit", 10, "the max number of results")// 变量绑定,当在命令行中指定 -limit 100 的时候,这意味着我们是把 100 这个值,赋予变量 limit标识(flag)赋予了我们通过命令行直接给代码中某个变量赋值的能力。那么一个新的问题是,如果我没有给这个变量赋值呢,程序还能继续运行下去吗?如果不能继续运行,则这个参数(flag 只是一种特殊的参数)就是必选的,否则就是可选的。还有一种可能,命令行定义了多个变量,任意一个变量有值,程序都可以执行下去,也即是说只要这多个标识中随便指定一个,程序就可以执行,那么这些标识或参数从这个角度讲又可以叫做选项(option)。
经过上面的分析,我们发现参数、标识、选项的概念彼此交织,既有区别又有相近的含义。标识是以横线开头的参数,标识名后面的参数(如果有的话),是标识的值。这些参数可能是必选或可选,或多个选项中的一个,因此这些参数又可以称为选项。
3 子命令
经过上面的分析,我们可以很简单的得出结论,子命令只是一种特殊的参数,这种参数外观上跟其他参数没有任何区别(不像标识用横线开头),但是这个参数会引发特殊的动作或函数(任意动作都可以封装为一个函数)。
对比标识和子命令我们会意外的发现其中的关联:标识关联变量而子命令关联函数!他们具有相同的目的,标识后面的参数,是变量的值,那么子命令后面的所有参数,就是这个函数的参数(并非指语言层面的函数参数)。
更有趣的问题是,为什么标识需要以横线开头?如果没有横线,是否能达成关联变量的目的?这显然可以的,因为子命令就没有横线,对变量的关联和对函数的关联并没有什么区别。本质上,这个关联是通过标识或子命令的名字实现的,那横线起到什么作用呢?
是跟变量关联还是函数关联,仍然是由参数的名字决定的,这是在代码中预先定义的,没有横线一样可以区别标识和子命令,一样可以完成变量或参数的关联。
比如:
// 不带有横线的参数也可以实现关联变量或函数for _, arg := range os.Args{ switch arg{ case "limit": // 设置 limit 变量 case "scan": // 调用 scan 函数 }}由此可见,标识在核心功能实现上,并没有特殊的作用,横线的作用主要是用来增强可读。然而需要注意的是,虽然本质上我们可以不需要标识,但一旦有了标识,我们就可以利用其特实现额外的功用,比如 netstat -lnt这里的 -lnt就是 -l -n -t的语法糖。
4 命令行的构成
经过上面的分析,我们可以把命令行的参数赋予不同的概念
标识(flag):以横线或双横线开头的参数,标识又由标识名和标识参数组成--flagname flagarg非标识参数子命令(subcommand),子命令也会有子命令,标识和非标识参数$ command --flag flagarg subcommand subcmdarg --subcmdfag subcmdflagarg四 启发式命令行解析我们来重新审视一下第一个需求,即我们期望任何一个子命令的实现,都跟使用标准库的 flag 一样简单。这也就意味着,只有在执行这个函数的时候,才开始解析其命令行参数。如果我们能把子命令和其他参数区分开来,那么就可以先执行子命令对应的函数,后解析这个子命令的参数。
flag 之所以在 main中调用 Parse, 是因为 shell 已经知道字符串的第一个项是命令本身,后面所有项都是参数,同样的,如果我们能识别出子命令来,那么也可以让以下代码变为可能:
func command(){ // 定义 flags // 调用 Parse 函数}问题的关键是如何将子命令跟其他参数区分开来,其中标识名以横线或双横线开头,可以显而易见的区别开来,其他则需要区分子命令、子命令参数以及标识参数。仔细思考可以发现,我们虽然期望参数无需预先定义,但子命令是可以预先定义的,通过把非标识名的参数,跟预先定义的子命令比对,则可以识别出子命令来。
为了演示如何识别出子命令,我们以上面 cobra 的代码为例,假设 cobra.go 代码编译为程序 app,那么其命令行可以执行
$ app echo times hello --times 3按 cobra 的概念, times 是 echo 的子命令,而 echo 又是 app 的子命令。我们则把 echo times整体作为 app 的子命令。
1 简单解析流程
定义echo子命令关联到函数echo, echo times子命令关联到函数 echoTimes解析字符串 echo times hello --times 3解析第一个参数,通过 echo匹配到我们预定义的 echo子命令,同时发现这也是 echo times命令的前缀部分,此时,只有知道后一个参数是什么,我们才能确定用户调用的是 echo还是 echo times解析第二个参数,通过 times我们匹配到 echo times子命令,并且其不再是任何子命令的前缀。此时确定子命令为 echo times,其他所有参数皆为这个子命令的参数。如果解析第二个参数为 hello,那么其只能匹配到 echo这个子命令,那么会调用 echo函数而不是 echoTimes函数。2 启发式探测流程
上面的解析比较简单,但现实情况下,我们往往期望允许标识可以出现在命令行的任意位置,比如,我们期望新加一个控制打印颜色的选项 --color red,从逻辑上讲,颜色选项更多的是对 echo的描述,而非对 times的描述,因此我们期望可以支持如下的命令行:
$ app echo --color red times hello --times 3此时,我们期望调用的子命令仍然是 echo times,然而中间的参数让情况变得复杂起来,因为这里的参数 red可能是 --color的标识参数(red),可能是子命令的一部分,也可能是子命令的参数。更有甚者,用户还可能把参数错误的写为 --color times
下一篇:返回列表
相关链接 |
||
网友回复(共有 0 条回复) |