65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 .NET 的 GetOpt

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.10/5 (6投票s)

2008 年 5 月 30 日

LGPL3

6分钟阅读

viewsIcon

53235

downloadIcon

1334

一个适用于 .NET 的 GetOpt 实现。

引言

"getopt" 在类 UNIX 操作系统上的 C 编程中是一个熟悉的函数,但在其之外(例如,Windows、Mac OS)几乎不存在。getopt 的目的是帮助程序员解析通过命令行传递给应用程序的选项及其参数。

CpGetOpt 是这个小项目的名称,它为 .NET 提供了一个(目前是部分的)getopt 实现,该实现是用 C# 编写的。CpGetOpt 符合 POSIX 规范,并且可以选择几乎完全模拟 glibc 版本的 getopt。目前,尚不支持长选项,但很快就会支持。

有关 getopt 的更多信息,请访问 glibc 文档,网址为 www.gnu.org

另外,请访问我的博客以查看我的更多作品

背景

Getopt 已经存在很长时间了,它有助于处理可能变成复杂任务的事情:解析程序参数,以及它们本身可以接受的参数。Getopt 是大多数 C 运行时库的一部分,并且在许多编程语言中都有实现。通常,getopt 只支持 Unix 风格的命令选项,但支持 Windows 风格的选项并不难实现(请参阅本节的最后一段)。

什么是选项?

选项允许程序员以更结构化的方式处理可选的程序参数。实质上,选项是特殊类型的命令行参数,旨在赋予程序本身中的标志或变量意义,顾名思义,它们是可选的。选项允许程序员以更结构化的方式处理可选的程序参数。

通过检查给定的程序参数数量并据此解释它们,也可以实现相同的功能。然而,显然,这要复杂得多且耗时,而使用 getopt 则简单得多。

如何通过命令行将选项传递给应用程序?

很简单;选项只需以破折号(“-”)为前缀,然后是一个标识所指定选项的字符,然后是该选项的参数(如果允许任何参数)。此外,可以通过两种不同的方式为选项提供参数。第一种方式是:选项后跟一个空格,然后是参数;第二种方式是:选项后直接跟参数,没有空格(使用这种形式时,选项字符必须紧跟在破折号后面,并且后面的字符本身不能是选项;如果“f”也是一个有效选项,则“-afoo”不会按预期工作)。

# In this example, both commands are equivalent.
Example:
    app.exe -a foo
    app.exe -afoo

此外,假设您使用第一种形式来指定传递给程序的选项,您可以将多个选项组合成一个字符串。

# In this example, both commands are equivalent.
Example:
    app.exe -abc
    app.exe -a -b -c

如果选项“b”和“c”需要参数,选项字符串可以保持不变,参数只需按各自的顺序跟随选项。

# In this example, both commands are equivalent.
Example:
    app.exe -abc foo bar
    app.exe -a -b foo -c bar

什么是长选项?

长选项与常规选项完全相同,只是它们的长度可以超过一个字符(即,--verbose 而不是 -v)。并且,当在选项字符串中包含选项参数时,您需要使用等号(“=”)将两者分开,且没有空格。

# For an application where options 'i' and 'include' have the same meaning,
# the commands below are all equivalent.
Example:
    app.exe -iarg
    app.exe -i arg  
    app.exe --include=arg 
    app.exe --include arg

不幸的是,CpGetOpt 尚不支持长选项,但很快就会支持。

"getopt" 是大多数 libc 库的标准组件,也是 POSIX 规范的一部分;但如前所述,它在微软的 C 运行时中缺失。Windows 在调用命令时确实使用选项的概念,但有一个细微的区别:不是用前缀 "-" 或长选项用 "--" 指定选项,所有选项都简单地以单个正斜杠 ("/") 为前缀;并且在提供带有选项本身的选项参数时,不是使用 "=",而是使用冒号 (":")。

    # UNIX-style
    app -a -b -c foo --longopt=arg
    # Windows-style
    app.exe /a /b /c foo /longopt:arg

使用代码

目前,CpGetOpt 定义了两种类型:GetOptionsSettingsGetOpt

GetOptionsSettings 提供了一个枚举,可以作为一组标志传递给 GetOpt.GetOptions 函数,以控制选项的解析方式。

  • GlibcCorrect - 指定 GetOptions 应该与 glibc 的 getopt 函数的行为完全一致。
  • PosixCorrect - 指定 GetOptions 应该以符合 POSIX 标准的方式运行。
  • ThrowOnError - 在解析过程中发生错误时抛出 ApplicationException
  • PrintOnError - 发生错误时,向命令行打印错误消息。
  • None - 未设置任何选项;使用默认行为。

GetOpt 是提供 getopt 实现的容器类。

GetOpt 类的 GetOptions 方法是用于连续解析命令行参数的方法,它接受三个参数,其中第三个是可选的。

int GetOptions(string[] args, string options, [GetOptionsSettings settings])
  • args - 一个字符串数组;由 Main 方法提供给您的参数数组。
  • options - 一个字符串,指定有效的选项及其参数要求。此字符串必须与原始 getopt 使用的字符串格式相同。“此字符串中的选项字符后可以跟一个冒号 (:),表示它需要一个必需的参数。如果选项字符后跟两个冒号 (::),则其参数是可选的。”
  • settings - GetOptionsSettings 枚举值的按位 OR 组合,可以可选地将解析方法更改为其他行为。

GetOptions 返回标识选项的字符(强制转换为 int),当当前选项导致错误时返回 '?',当所有选项都被解析后返回 -1。

默认情况下,每次成功调用 GetOptions 后,可以通过 GetOpt.Item 属性访问选项名称。但是,当指定 GetOptionsSettings.GlibcCorrect 时,此行为仅在解析导致错误的选项时才成立。

如果返回的选项带有参数,则可以通过 GetOpt.Text 属性访问该参数。

解析所有选项并由 GetOptions 返回 -1 后,访问 GetOpt.Index 属性以获取 args 数组中的索引,您可以从该索引恢复正常的参数处理。

重要提示:GetOptions 方法维护的所有状态信息都是线程静态的。这意味着在不同执行线程中对 GetOptions 的调用将相互独立。

那么它是如何工作的?

很简单;在循环中调用 GetOpt.GetOptions 直到它返回 -1,并使用返回的字符来处理传递给应用程序的选项。

//
// Normally, getopt is called in a loop. When getopt returns -1, indicating
// no more options are present, the loop terminates.
//
// A switch statement is used to dispatch on the return value from getopt.
// In typical use, each case just sets a variable that is used later in the program.
//
// A second loop is used to process the remaining non-option arguments. 
//

// Make sure to add CpGetOpt.dll as an assembly reference in your project
// and then just add a "using CodePoints;" statement.
using CodePoints;
using System;

...
public static void Main ( string [] args ) {
    int c = 0, aflag = 0, bflag = 0;
    string cvalue = "(null)";

    while ( ( c = GetOpt.GetOptions(args, "abc:") ) != ( -1 ) ) {
        switch ( ( char ) c ) {
            case 'a':
                aflag = 1;
                break;
            case 'b':
                bflag = 1;
                break;
            case 'c':
                cvalue = GetOpt.Text;
                break;
            case '?':
                Console.WriteLine("Error in parsing option '{0}'", GetOpt.Item);
                break;
            default:
                return;
        }
    }

    Console.WriteLine("aflag = {0}, bflag = {1}, cvalue = {2}", aflag, bflag, cvalue);

    for ( int n = GetOpt.Index ; n < args.Length ; n++ )
        Console.WriteLine("Non-option argument: {0}", args [n]);
}
...

以下是一些示例,展示了此程序在不同参数组合下的输出

% testopt.exe
aflag = 0, bflag = 0, cvalue = (null)

% testopt.exe -a -b
aflag = 1, bflag = 1, cvalue = (null)

% testopt.exe -ab
aflag = 1, bflag = 1, cvalue = (null)

% testopt.exe -c foo
aflag = 0, bflag = 0, cvalue = foo

% testopt.exe -cfoo
aflag = 0, bflag = 0, cvalue = foo

% testopt.exe arg1
aflag = 0, bflag = 0, cvalue = (null)
Non-option argument: arg1

% testopt.exe -a arg1
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument: arg1

% testopt.exe -c foo arg1
aflag = 0, bflag = 0, cvalue = foo
Non-option argument: arg1

% testopt.exe -a -- -b
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument: -b

% testopt.exe -a -
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument: -

关注点

如果您想了解我是如何实现 getopt 的,请查看源代码。我想说的是:正确且功能性地实现 getopt 并不像乍看起来那么容易。无论如何,我希望有人能够利用并发现 CpGetOpt 的一些用途。

© . All rights reserved.