天天看点

使用 getopt() 、getopt_long()、getopt_long_only()进行命令行处理

 简介: 所有 UNIX® 程序甚至那些具有图形用户界面(graphical user interface,GUI)的程序,都能接受和处理命令行选项。对于某些程序,这是与其他程序或用户进行交互的主要手段。具有可靠的复杂命令行参数处理机制,会使得您的应用程序更好、更有用。不过很多开发人员都将其宝贵的时间花在了编写自己的命令行解析器,却不使用 <code>getopt()</code>,而后者是一个专门设计来减轻命令行处理负担的库函数。请阅读本文,以了解如何让 <code>getopt()</code> 在全局结构中记录命令参数,以便随后随时在整个程序中使用。

<a>引言</a>

在早期的 UNIX® 中,其命令行环境(当时的唯一用户界面)包含着数十种小的文本处理工具。这些工具非常小,通常可很好地完成一项工作。这些工具通过较长的命令管道链接在一起,前面的程序将其输出传递给下一个程序以作为输入,整个过程由各种命令行选项和参数加以控制。

正是 UNIX 的这方面的特征使其成为了极为强大的处理基于本文的数据的环境,而这也是其在公司环境中的最初用途之一。在命令管道的一端输入一些文本,然后在另一端检索经过处理的输出。

命令行选项和参数控制 UNIX 程序,告知它们如何动作。作为开发人员,您要负责从传递给您程序的 <code>main()</code> 函数的命令行发现用户的意图。本文将演示如何使用标准 <code>getopt()</code> 和 <code>getopt_long()</code> 函数来简化命令行处理工作,并讨论了一项用于跟踪命令行选项的技术。

<a>开始之前</a>

<a>命令行</a>

<a>清单 1. 声明 <code>main()</code> 函数的两种方式</a>

第一种方式使用的是指向 <code>char</code> 指针数组,现在似乎很流行这种方式,比第二种方式(其指针指向多个指向 <code>char</code> 的指针)略微清楚一些。由于某些原因,我使用第二种方式的时间更多一些,这可能源于我在高中时艰难学习 C 指针的经历。对于所有的用途和目的,这两种方法都是一样的,因此可以使用其中您自己最喜欢的方式。

当 C 运行时库的程序启动代码调用您的 <code>main()</code> 时,已经对命令行进行了处理。<code>argc</code> 参数包含参数的计数值,而 <code>argv</code> 包含指向这些参数的指针数组。对于 C 运行时库,arguments 是程序的名称,程序名后的任何内容都应该使用空格加以分隔。

<a>清单 2. argv 的内容</a>

示例代码将演示一个假想的 doc2html 程序的命令行处理。该 doc2html 程序将某种类型的文档转换为 HTML,具体由用户指定的命令行选项控制。它支持以下选项:

<code>-I</code>——不创建关键字索引。

<code>-l lang</code>——转换为使用语言代码 <code>lang</code> 指定的语言。

<code>-o outfile.html</code>——将经过转换的文档写入到 outfile.html,而不是打印到标准输出。

<code>-v</code>——进行转换时提供详细信息;可以多次指定,以提高诊断级别。

将使用其他文件名称来作为输入文档。

您还将支持 <code>-h</code> 和 <code>-?</code>,以打印帮助消息来提示各个选项的用途。

<a>简单命令行处理: <code>getopt()</code></a>

<a>清单 3. <code>getopt()</code> 原型</a>

给定了命令参数的数量 (<code>argc</code>)、指向这些参数的数组 (<code>argv</code>) 和选项字符串 (<code>optstring</code>) 后,<code>getopt()</code> 将返回第一个选项,并设置一些全局变量。使用相同的参数再次调用该函数时,它将返回下一个选项,并设置相应的全局变量。如果不再有识别到的选项,将返回<code>-1</code>,此任务就完成了。

<code>getopt()</code> 所设置的全局变量包括:

<code>optarg</code>——指向当前选项参数(如果有)的指针。

<code>optind</code>——再次调用 <code>getopt()</code> 时的下一个 argv 指针的索引。

<code>optopt</code>——最后一个已知选项。

对于每个选项,选项字符串 (<code>optstring</code>) 中都包含一个对应的字符。具有参数的选项(如示例中的 <code>-l</code> 和 <code>-o</code> 选项)后面跟有一个 <code>:</code> 字符。示例所使用的 <code>optstring</code> 为 <code>Il:o:vh?</code>(前面提到,还要支持最后两个用于打印程序的使用方法消息的选项)。

可以重复调用 <code>getopt()</code>,直到其返回 <code>-1</code> 为止;任何剩下的命令行参数通常视为文件名或程序相应的其他内容。

<a><code>getopt()</code> 的使用</a>

<a>清单 4. 系统头文件 </a>

每个命令行选择都有一个对应的选项,而其他变量用于存储输出文件名、指向输入文件列表的指针和输入文件数量。

<a>清单 5. 全局参数存储和选项字符串</a>

选项字符串 <code>optString</code> 告知 <code>getopt()</code> 可以处理哪个选项以及哪个选项需要参数。如果在处期间遇到了其他选项,<code>getopt()</code> 将显示一个错误消息,程序将在显示了使用方法消息后退出。

<a>清单 6. 存根</a>

<a>清单 7. 初始化</a>

<a>清单 8. 使用 <code>getopt()</code> 处理 argc/argv</a>

<a>清单 9. 开始工作</a>

好,工作完成,非常漂亮。现在就可以不再往下读了。不过,如果您希望程序符合 90 年代末期的标准并支持 GNU 应用程序中流行的长 选项,则请继续关注下面的内容。

<a>复杂命令行处理: <code>getopt_long()</code></a>

在 20 世纪 90 年代(如果没有记错的话),UNIX 应用程序开始支持长选项,即一对短横线(而不是普通短 选项所使用的单个短横线)、一个描述性选项名称还可以包含一个使用等号连接到选项的参数。

幸运的是,可以通过使用 <code>getopt_long()</code> 向程序添加长选项支持。您可能已经猜到了,<code>getopt_long()</code> 是同时支持长选项和短选项的<code>getopt()</code> 版本。

<a>清单 10. <code>getopt_long()</code> 的选项</a>

<code>name</code> 成员是指向长选项名称(带两个短横线)的指针。<code>has_arg</code> 成员设置为 <code>no_argument</code>、<code>optional_argument</code>, 或<code>required_argument</code>(均在 <code>getopt.h</code> 中定义)之一,以指示选项是否具有参数。如果 flag 成员未设置为 NULL,在处理期间遇到此选项时,会使用 <code>val</code> 成员的值填充它所指向的 <code>int</code> 值。如果 flag 成员为 <code>NULL</code>,在 <code>getopt_long()</code> 遇到此选项时,将返回 <code>val</code> 中的值;通过将 <code>val</code> 设置为选项的 <code>short</code> 参数,可以在不添加任何其他代码的情况下使用 <code>getopt_long()</code>——处理 <code>while loop</code> 和 <code>switch</code> 的现有 <code>getopt()</code> 将自动处理此选项。

这已经变得更为灵活了,因为各个选项现在可以具有可选参数了。更重要的是,仅需要进行很少的工作,就可以方便地放入现有代码中。

<a>使用 <code>getopt_long()</code></a>

由于 getopt_long_demo 几乎与刚刚讨论的 getopt_demo 代码一样,因此我将仅对更改的代码进行说明。由于现在已经有了更大的灵活性,因此还将添加对 <code>--randomize</code> 选项(没有对应的短选项)的支持。

<a>清单 11. 其他头文件</a>

<a>清单 12. 扩展后的参数</a>

<a>清单 13. 新的经改进的选项处理</a>

我还添加了 <code>0</code> 的 case,以便处理任何不与现有短选项匹配的长选项。在此例中,只有一个长选项,但代码仍然使用 <code>strcmp()</code> 来确保它是预期的那个选项。

这样就全部搞定了;程序现在支持更为详细(对临时用户更加友好)的长选项。

<a>总结</a>

UNIX 用户始终依赖于命令行参数来修改程序的行为,特别是那些设计作为小工具集合 (UNIX 外壳环境)的一部分使用的实用工具更是如此。程序需要能够快速处理各个选项和参数,且要求不会浪费开发人员的太多时间。毕竟,几乎没有程序设计为仅处理命令行参数,开发人员更应该将精力放在程序所实际进行的工作上。

<code>getopt()</code> 函数是一个标准库调用,可允许您使用直接的 while/switch 语句方便地逐个处理命令行参数和检测选项(带或不带附加的参数)。与其类似的 <code>getopt_long()</code> 允许在几乎不进行额外工作的情况下处理更具描述性的长选项,这非常受开发人员的欢迎。

既然已经知道了如何方便地处理命令行选项,现在就可以集中精力改进您的程序的命令行,可以添加长选项支持,或添加之前由于不想向程序添加额外的命令行选项处理而搁置的任何其他选项。

不要忘记在某处记录您所有的选项和参数,并提供某种类型的内置帮助函数来为健忘的用户提供帮助。

<a href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#ibm-pcon">回页首</a>

<a>下载</a>

描述

名字

大小

下载方法

Sample getopt() program

au-getopt_demo.zip

23KB

<a href="http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=249919&amp;filename=au-getopt_demo.zip&amp;method=http&amp;locale=zh_CN">HTTP</a>

Sample getopt_long() program

au-getopt_long_demo.zip

24KB

<a href="http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=249919&amp;filename=au-getopt_long_demo.zip&amp;method=http&amp;locale=zh_CN">HTTP</a>

<a href="http://www.ibm.com/developerworks/cn/whichmethod.html">关于下载方法的信息</a>

     本文转自 驿落黄昏 51CTO博客,原文链接:http://blog.51cto.com/yiluohuanghun/1182459,如需转载请自行联系原作者

继续阅读