设计命令行参数的良好习惯是什么?


190

在开发应用程序时,我开始怀疑-我应该如何设计命令行参数?

很多程序都使用公式是这样-argument value/argument value。我想到的解决方案是argument:value。我认为这是很好的,因为没有空格就无法将值和参数弄乱。同样,很容易将字符串从左:字符的第一个字符拆分为两个。

我的问题是:

  1. 流行的-argument value公式是否比argument:value(更易读,更容易编写,没有错误,更易于专家开发人员理解)更好?
  2. 在设计命令行参数时,是否应该遵循一些众所周知的规则(如果可行,可以的话)?

要求提供更多详细信息,我将提供它。但是,我认为它们不应影响答案。问题在于总体上的良好习惯。我认为它们对于各种应用程序都是相同的。

我们正在开发一个将在公共场所(触摸图腾,桌子)使用的应用程序。应用程序是使用Qt Quick 5(C ++,QML,JS)编写的。设备将安装Windows 8.1 / 10。我们将提供前端界面来管理设备。但是,某些高级管理员可能希望自己配置应用程序。从业务的角度来看这不是很重要,但是正如我同意Kilian Foth所说的那样,我不希望我的应用程序给用户带来麻烦。在互联网上找不到我想要的内容。


对于更高级的Stack Exchange用户:我希望这个问题笼统。也许它符合社区Wiki的资格(我不知道是否可以将现有问题转换为答案)。因为我希望这个问题与操作系统和编程语言无关,所以这里出现的答案对于其他开发人员来说可能是一个宝贵的教训。


14
查看流行的命令行工具。例如,单个连字符通常用于允许组合选项。例如,你可以写ls -ltr在选项组合-l-t-r。GNU样式程序通常还允许基于单词的选项使用双连字符,例如--reverse代替-r。还有其他流行的约定,例如-h显示帮助,--表示选项结束,指定-为文件名以允许从stdin读取等
。– Brandin

72
都不是-argument value-argument:value常见的。常见的有-a value-avalue--argument=value
reinierpost

39
尽可能使用流行的命令行解析库(通常称为getopt(s))。
reinierpost

5
@ k3b我们正在与Qt合作。正如kevin cline他的评论中所说,我们可以使用已经可用的库。我认为它是多平台且经过深思熟虑的。QCommandLineParser
Filip Hazubski

11
最终的问题是参数解析不是平台无关的问题。
pydsigner

Answers:


237

在POSIX系统(例如Linux,MacOSX)上,至少对于可能在Shell终端中启动的程序(例如其中的大多数),我建议使用GNU编码约定(还列出了常用的参数名称),并查看POSIX实用程序指南,即使是专有软件:

  • 总是处理--version--help(甚至/bin/true接受它们!)。我诅咒软件作者不理解--help,我讨厌他们(因为这prog --help 是我在新程序上尝试的第一个命令)!通常--help可以缩写为-h

  • 使--help消息列出所有选项(除非您有太多的选择...在这种情况下,列出最常见的选项并显式引用某些man页面或某些URL)和选项的默认值,并且可能是重要的(且特定于程序) ) 环境变量。在选项参数错误时显示这些选项列表。

  • 接受-a简短的论点(单字母)并具有一些等效项--long-argument,因此-a2 --long-argument=2--long-argument 2;; 当然,您可以(对于很少使用的选项)使用一些--only-long-argument名称;对于没有额外选项的模态参数,-cf通常会以-c -f等方式处理,因此您的-argument:value建议很奇怪,我不建议您这样做。

  • 使用GLIBC getopt_long或更好(例如argp_parse,在OCaml中是Arg模块,...)

  • 经常使用-标准输入或输出(如果你不能做到这一点,处理/dev/stdin/dev/stdout甚至在一些操作系统没有他们)

  • 通过重用它们的大多数选择约定来模仿相似程序的行为;特别是-n对于空转(àla make),-h寻求帮助,-v冗长等...

  • 使用--作为选项-文件或其他参数之间的分隔符

  • 如果您的程序用于isatty测试而不是stdin是终端(并且在这种情况下表现为“交互”),则提供一个选项来强制非交互模式,同样,如果您的程序具有GUI界面(并getenv("DISPLAY")在X11桌面上进行测试),但也可以用于批处理或命令行。

  • 一些程序(例如gcc)接受间接参数列表,所以@somefile.txt从中读取程序参数的含义也是如此somefile.txt。当你的程序可能会接受一个非常大的很多参数,这可能是有用的(超过你的内核ARG_MAX

顺便说一句,您甚至可以为程序和常规shell(如bashzsh)添加一些自动完成功能

一些旧的Unix命令(例如dd,甚至sed)具有奇怪的命令参数以实现历史兼容性。我建议不要遵循他们的坏习惯(除非您对它们进行了一些改进)。

如果你的软件是一系列相关的命令行程序的,采取从灵感混帐(这你一定作为开发工具使用),它接受git helpgit --help并有许多gitsubcommandgitsubcommand--help

在极少数情况下,您可能还会使用argv[0](通过在程序上使用符号链接),例如,bash由于rbash具有不同的行为(受限制的 shell)而被调用。但是我通常不建议这样做;如果可以使用shebang将您的程序用作脚本解释器,即#!execve(2)解释的第一行上,那可能就有意义。如果您使用这些技巧,请确保将它们记录下来,包括在--help消息中。

请记住,在POSIX上,外壳程序遍历参数(运行程序之前!),因此请避免在需要转义外壳程序的选项中要求使用字符(例如*$~)。

在某些情况下,您可以在软件中嵌入诸如GNU guileLua之类的解释器(如果您不是编程语言专家,请避免发明自己的图灵完整脚本语言)。这会对软件的设计产生深远的影响(因此应尽早考虑!)。然后,您应该可以轻松地将某些脚本或某些表达式传递给该解释器。如果您采用这种有趣的方法,请谨慎设计软件及其解释的原语;您可能会有一些奇怪的用户为您的事情编写大型脚本。

在其他情况下,您可能希望让高级用户将其插件加载到您的软件中(使用动态加载技术àla dlopendlsym)。同样,这是一个非常重要的设计决策(因此,请谨慎定义和记录插件接口),并且您需要定义一个约定以将程序选项传递给这些插件。

如果您的软件比较复杂,请使其接受某些配置文件(附加或替换程序参数),并且可能有某种方法可以测试(或只是解析)这些配置文件而无需运行所有代码。例如,邮件传输代理(例如Exim或Postfix)非常复杂,能够“半干”运行它(例如观察它如何处理某些给定的电子邮件地址而不实际发送电子邮件)很有用。


注意,这/option是Windows或VMS。在POSIX系统上这太疯狂了(因为文件层次结构/用作目录分隔符,并且因为shell会进行globbing)。我所有的答案主要是针对Linux(和POSIX)的。


PS:如果可能的话,使您的程序成为免费软件,您会从某些用户和开发人员那里得到改进(添加新程序选项通常是添加到现有免费软件中最容易的事情之一)。此外,你的问题取决于很多的目标受众:青少年游戏或奶奶的浏览器可能并不需要相同种类和比一个编译器选项量,或用于数据中心的系统管理员的网络检查员,或微处理器CAD软件建筑师或桥梁设计师。熟悉编程和脚本的工程师可能比祖母更喜欢具有许多可调选项,并且可能希望能够在没有X11的情况下运行您的应用程序(也许正在crontab工作)。


18
同意,但这git 是一个更好的例子。我甚至不会建议寻找cvs在2016年
巴西莱Starynkevitch

8
+如果选项无效,请显示帮助。收到例如的无用错误消息实在令人讨厌dd -h
domen

8
GNU程序--help通常支持,但是通常不知道-h哪个令人讨厌。当我忘记程序的选项时,通常会输入-h,而不得不用更长的option来重新输入命令很烦人--help。毕竟,我键入-h是因为忘记了一些内容。为什么要让用户记住哪些程序需要“ --help”,哪些程序需要“ -h”以显示帮助屏幕?只需为-h和--help都包含一个选项即可。
布兰丁

30
这个答案的第一部分是好的,但是沿着您的某个地方,切线会有些偏离。例如,配置文件,插件和dlopen,软件许可证的选择和Web浏览器不再与命令行界面的约定真正相关。
布兰丁

5
拜托了 如果确实要格式化屏幕输出,请添加输出格式替代。当我尝试在脚本中使用截断或换行的命令时,没有什么比让我感到烦恼的了。
Sobrique '16

68

数据格式约定很流行的事实它的优点。

您可以轻松地看到,使用=或:或什至''作为分隔符是微不足道的差异,这些差异可以由计算机轻松转换为彼此。这将是一个很大的努力,是任何一个人记住“现在看,这样做是不常用的程序划定事情:=?嗯...”

换句话说,出于对上帝的爱,在没有令人信服的理由的情况下,请不要背离极其根深蒂固的惯例。人们会把您的程序记为“具有奇怪而烦人的cmdline语法的程序”,而不是“保存了我大学论文的程序”。


19
对于几乎所有语言,都有库函数来处理命令行参数。使用其中之一。
凯文·克莱恩

9
好的建议,但是这些约定是什么?-1
RubberDuck

14
有一个有趣的常用UNIX工具示例,它故意违反了该约定:dd使用key=value语法。这样做的原因设计决策的是,这个工具(绰号:d ATA d estroyer)不正确地使用时,会造成大量的损失。通过强迫用户放弃他们的惯常习惯,他们迫使用户更加仔细地思考他们在做什么。
菲利普

18
您确定这是原因dd吗?我相信它只是在某个时间(1970年代?)编码的,当时内核的ARG_MAX很小,shell没有自动完成功能,并且--long-arguments不存在。从那时起,更好地dd保持了向后兼容性
Basile Starynkevitch

9
dd起源于另一个OS(而不是UNIX)-IBM OS / 360上的脚本语言(JCL)-约定有所不同,在移植之前或多或少未更改到UNIX之前,部分原因是那些可能使用它的人从以前的系统。
巴德·科珀罗德

29

用外行的话

在罗马做到入乡随俗。

  • 如果您的CLI应用程序适用于Linux / Unix,请使用-p value--parameter value约定。Linux具有用于轻松解析此类参数和标志的工具。

我通常会这样:

while [[ $# > 0 ]]
do
key="$1"
case $key in
    --dummy)
    #this is a flag do something here
    ;;
    --audit_sessiones)
    #this is a flag do something here
    ;;
    --destination_path)
    # this is a key-value parameter
    # the value is always in $2 , 
    # you must shift to skip over for the next iteration
    path=$2
    shift
    ;;
    *)
    # unknown option
    ;;
esac
shift
done
  • 如果您的CLI应用程序适用于Windows,请使用/flag/flag:value约定。

  • 像Oracle这样的某些应用程序却没有使用。Oracle实用程序使用PARAMETER=VALUE

  • 我想做的一件事是,除了在命令行中接受参数外,还提供使用parfile的选项,parfile是一个键值对文件,以避免冗长的参数链。为此,您应该提供一个附加--parfile mifile.par参数。显然,如果--parfile使用,则所有其他参数都将被丢弃,以支持parfile中的内容。

  • 另一个建议是允许使用某些自定义环境变量,例如,设置环境变量MYAPP_WRKSPACE=/tmp将使其不必始终设置--wrkspace /tmp

  • 在Linux中,不要忘了添加参数auto-completion,这意味着用户可以键入一半的开关,TAB然后按一下,然后shell会为他们完成它。

1
好吧,几个GNU实用程序(例如gcc)的处理@mifile.par就像您的--parfile mifile.par建议一样。
Basile Starynkevitch

7
如果确定有参数文件,您确定要忽略所有其他选项吗?充其量听起来对我而言是违反直觉的。
乔纳森·莱夫勒

8
我同意乔纳森(Jonathan)关于参数文件的观点。如果存在参数文件,那么我希望它可以用作默认值,命令行上给出的参数应放在parfile中。如果由于某种原因使用了parfile而不使用附加的命令行参数,则附加参数的存在应该是错误的。
Eldritch奶酪

@EldritchCheese在我编写的CLI应用程序中,给定parfile时,任何附加参数都会产生错误。
TulainsCórdova'16

1
如果使用有能力的外壳,则不需要这种“配置文件参数”选项。例如你写fooCmd -opt1 -opt2 $(cat more_options.opts)。因此,我希望“配置文件参数”选项(如果提供)应该基本上以相同的方式工作。
布兰丁

19

尚未出现的一件事:

尝试从命令行参数向上设计软件。含义:

在设计功能之前,请设计用户界面。

这样,您就可以尽早深入分析边缘案例和常见案例。当然,您仍然要对外部和内部进行抽象,但是与只编写所有代码然后对它进行CLI抨击相比,它会产生更好的结果。

此外,请查看docopt(http://docopt.org/)。

docopt在许多语言中都提供了很大的帮助,尤其是对于python而言,它的使用非常有限,像argparse这样的用户不利参数解析器仍然被认为是“ OK”。无需定义解析器,子解析器和条件字典,您只需定义语法帮助即可完成其余工作。


我喜欢这个答案,并感谢您的回答,因为我目前对argparse没有程序员,用户界面或用户友好性感到沮丧。
2016年

我尝试了docopt,但我不喜欢它。单击在结果多少干净的代码,虽然有一点点wonkiness的选项,当你有子。
jpmc26 2016年

2
这太像广告了。只需提及一次资源(如果相关)。但是就目前而言,您答案的50%听起来像是在推广外部资源。
布兰丁

我将其表述为“首先设计使用模型”。在许多情况下,将用户体验与功能区分开可能是人为的区分(工具限制会影响界面)。
Copper.hat

1
Docopt +1。它解决了我所有的CLI难题,完全没有痛苦。有时候很难从真正的热情中分辨出广告,但是在这里-我多年来一直是Docopt的狂热者,没有任何隶属关系;)
frnhr 2016年

3

已经提供了一些有价值的评论(@ Florian,Basile),但让我补充一下……OP说,

我们将提供前端界面来管理设备。但是,某些高级管理员可能希望自己配置应用程序

还要说明:

我不希望这个问题与平台或语言有关

您必须考虑目标受众的高级管理员。他们通常在什么平台上工作-Win / Unix / Mac?您的应用在什么平台上运行?遵循已为该平台建立的任何CLI约定。您的“高级”管理员是否需要/需要基于GUI的工具?

您希望界面在内部和与其他管理工具保持一致。我不想停下来想一想是cmd -p <arg>or cmd -p:<arg>or cmd /p <arg>。我需要引号'cos那里有空格吗?我可以cmd -p <val1> <val2>还是cmd -p <val1> -p <val2>针对多个目标?它们是特定于订单的吗?可重载?不cmd -p2 <arg> -p1 <arg>工作吗?是否ls -l -r -t dir1 dir2== ls -trl dir1 dir2

对于我的Unix管理工具,我始终牢记Heiner's Shelldorado提供的指导以及提到的其他参考。

与设计CLI一样重要的是要确保您的应用程序设计为可以使用与GUI相同的命令行参数-即:GUI中没有业务逻辑,或者使用从GUI和CLI调用的通用命令。

实际上,大多数基于UNIX的管理工具最初都是作为命令行工具设计的,并且提供的GUI只是简化了“填充”命令行选项的工作。这种方法可以实现自动化,响应文件的使用等,以及不需动手的管理(对我而言,工作量更少!)

作为使用此方法的经典工具集,它是Tcl / Tk。不建议您切换工具;只需考虑以下设计方法:首先将基于GUI的管理应用程序编写为命令行工具即可;然后为方便起见将GUI放在最上面。在某些时候,如果您必须进行多个配置并一遍又一遍地重新输入相同的选项,并且可能会发现GUI是一种痛苦(并且容易出错),并且您会寻找一种自动化的方法。

请记住,无论如何,您的管理员可能都必须在正确的框中键入正确的值,所以您需要付出多少努力才能使用GUI?


太好了!一个问题也是:我可以运行./this-script --hosts <hosts.txt
Florian Heigl
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.