命令行选项的约定

2020-08-02 00:44:31

命令行界面在其短暂的历史中有所不同,但在很大程度上汇聚成了一些共同的、完善的约定。内核起源于Unix,Linux生态系统对其进行了扩展,特别是通过GNU项目。不幸的是,有些工具一开始似乎遵循惯例,但却巧妙地犯了错,通常没有实际好处。我相信在很多情况下,作者并不了解更多,所以我想回顾一下这些约定。

最简单的情况是做空期权标志。选项是一个连字符(具体地说是连字符减去U+002D),后跟一个字母数字字符。可以使用大写字母。这些信件本身具有传统意义,如果可能的话,值得遵循。

可以将标志一起分组到一个程序参数中。这既方便又明确。当程序使用手工编码的参数解析器时,这也是经常遗漏的细节之一,缺乏对它的支持使我感到不安。

空格是可选的,因此选项和参数可以打包到一个程序参数中。因为参数是必需的,所以这仍然是模棱两可的。这是手工编码解析器中另一个经常遗漏的特性。

此技术用于创建另一个类别,可选选项参数。选项的参数可以是可选的,但仍然是明确的,只要参数存在时始终省略空格。

Program-c#省略Program-cBlue#ProvidedProgram-c Blue#省略(Blue是新参数)Program-c-x#两个单独的标志Program-c-x#-c带有参数";-x";

可选选项参数应该明智地使用,因为它们可能会让人感到不安,但它们也有其用处。

选项通常可以以任何顺序出现-解析器通常通过排列来实现-但非选项通常跟随在选项之后。

GNU风格的程序通常允许选项和非选项混合在一起,尽管我不认为这是必要的。

如果非选项看起来像选项,因为它以连字符开头,请使用--来区分选项和非选项。

要求非期权紧跟在期权之后的一个好处是,第一个非期权区分了两组,因此--需要的次数较少。

#注:无参数置换程序-a-b foo-x bar#2个选项,3个非选项。

由于短选项可能是隐晦的,而且数量有限,因此更复杂的程序支持长选项。长选项以两个连字符开头,后跟一个或多个字母数字小写单词。用连字符分隔单词。使用两个连字符可防止将长选项与分组的短选项混淆。

有时,标志与以--no-开头的互斥反向标志配对。这避免了将来的标志日,在版本中更改了默认设置,同时添加了实现原始行为的标志。

可以选择使用等号=将它们连接到参数,这与省略短选项参数的空格非常相似。

与以前一样,这为可选选项参数打开了大门。由于需要,这一点仍然是明确的。

有些程序(如Git)有子命令,每个子命令都有自己的选项。主程序本身可能仍然具有有别于子命令选项的自己的选项。程序的选项在子命令之前,子命令选项在子命令之后。子命令周围的选项从未改变过。

上面的-a、-b和-c选项用于程序,其他选项用于子命令。所以,实际上,子命令是它自己的另一个命令行。

假设你对遵循这些约定感兴趣,那么就没有理由不正确地遵循这些约定。只需大约60行C代码就可以正确解析短选项。多头期权只是稍微复杂一点。

GNU的getopt_long()支持长选项缩写-无法禁用(!)-但这应该避免。

GO的FLAG包故意与约定背道而驰,它只支持长选项语义(通过单个连字符)。这使得即使所有选项都只有一个电子邮件,也不可能支持分组。此外,将选项和参数组合为单命令行参数的唯一方法是使用=。它听起来不错,但每次我用围棋编写程序时,我都会错过这两个功能。这就是为什么我写了我自己的论证解析器。它不仅有更好的功能集,我也更喜欢它的API。

Python的主要选项解析库是argparse,我简直无法忍受。尽管表面上看起来很循规蹈矩,但实际上它打破了惯例,其行为也是不健全的。例如,以下程序有两个选项--foo和--bar。Foo选项接受可选参数,而--bar选项是一个简单的标志。

$python parse.pyNamespace(bar=false,foo=';X';)$python parse.py--fooNamespace(bar=false,foo=None)$python parse.py--foo=argNamespace(bar=false,foo=#39;arg';)$python parse.py--bar--bar--fooNamespace(bar=True,foo=None)$。

除了最后一件,其他的看起来都很好。如果--foo参数是可选的,那么它为什么要消耗arg呢?如果我跟在后面加--bar会怎么样?它会把它当作论据吗?

不要啊!与arg不同的是,它没有使用--bar,因此它没有遵循明确的约定,而是具有自己的模糊语义,并试图用一个“智能”启发式来解决这些问题:“如果一个可选参数看起来像一个选项,那么它一定是一个选项!”非选项参数永远不能跟在带有可选参数的选项后面,这使得该功能非常无用。因为argparse不能正确支持--,所以这没有什么帮助。

$python parse.py--foo--arguse:parse.py[-h][--foo[foo]][--bar]parse.py:错误:无法识别的参数:--arg。

对这篇文章有什么评论吗?通过发送电子邮件至~Skeeto/[email protected][邮件列表礼仪]开始我的公共收件箱中的讨论,或查看现有讨论。