getopt 不将可选参数解析为参数
Posted
技术标签:
【中文标题】getopt 不将可选参数解析为参数【英文标题】:getopt does not parse optional arguments to parameters 【发布时间】:2010-11-06 08:50:12 【问题描述】:在 C 中,getopt_long 不会将可选参数解析为命令行参数参数。
当我运行程序时,无法识别可选参数,就像下面运行的示例一样。
$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !
这是测试代码。
#include <stdio.h>
#include <getopt.h>
int main(int argc, char ** argv )
int getopt_ret, option_index;
static struct option long_options[] =
"praise", required_argument, 0, 'p',
"blame", optional_argument, 0, 'b',
0, 0, 0, 0 ;
while (1)
getopt_ret = getopt_long( argc, argv, "p:b::",
long_options, &option_index);
if (getopt_ret == -1) break;
switch(getopt_ret)
case 0: break;
case 'p':
printf("Kudos to %s\n", optarg); break;
case 'b':
printf("You suck ");
if (optarg)
printf (", %s!\n", optarg);
else
printf ("!\n", optarg);
break;
case '?':
printf("Unknown option\n"); break;
return 0;
【问题讨论】:
我在这里记录这个答案,所以其他人不需要用头撞墙。 【参考方案1】:虽然在 glibc 文档或 getopt 手册页中未提及,但长样式命令行参数的可选参数需要“等号”(=)。将可选参数与参数分开的空格不起作用。
使用测试代码运行的示例:
$ ./respond --praise John Kudos to John $ ./respond --praise=John Kudos to John $ ./respond --blame John You suck ! $ ./respond --blame=John You suck , John!
【讨论】:
请注意,Perl 的 Getopt::Long 模块没有相同的要求。 Boost.Program_options 可以。 哇,这太糟糕了。我在使用常规 getopt() 时遇到了同样的问题,当使用 optstring“a::”时,只有在选项和参数之间有零空格(例如 '-afoo')时才会设置 optarg 至少对于我的短选项,如-a
或-a=300
,我需要添加一个if (optarg[0] == '=') memmove(optarg, optarg+1, strlen(optarg));
以去除=
。否则,我在optarg
中总是会有=300
。或者我需要这样称呼它:-a300
- 我认为这很丑陋。无论如何感谢您的回答,它对我帮助很大!
在这种情况下最好不要使用可选参数。这是错误还是功能?现在我有一种冲动./respond --blame=glibc
。
我想投反对票,但后来意识到您的回答很棒;这只是我想反对的行为。谢谢!【参考方案2】:
手册页当然没有很好地记录它,但源代码有一点帮助。
简而言之:您应该执行以下操作(尽管这可能有点过于迂腐):
if( !optarg
&& optind < argc // make sure optind is valid
&& NULL != argv[optind] // make sure it's not a null string
&& '\0' != argv[optind][0] // ... or an empty string
&& '-' != argv[optind][0] // ... or another option
)
// update optind so the next getopt_long invocation skips argv[optind]
my_optarg = argv[optind++];
/* ... */
来自 _getopt_internal 之前的 cmets:
...
如果
getopt
找到另一个选项字符,则返回该字符, 更新optind
和nextchar
以便下次调用getopt
可以 使用以下选项字符或 ARGV 元素恢复扫描。如果没有更多选项字符,
getopt
返回 -1。 然后optind
是第一个 ARGV 元素的 ARGV 中的索引 这不是一个选择。 (ARGV 元素已被置换 以便那些不是选项的选项现在排在最后。)<-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted
...
如果 OPTSTRING 中的字符后跟冒号,则表示它需要一个 arg, 所以在同一个 ARGV 元素中的以下文本,或以下文本 ARGV 元素,在
optarg
中返回。两个冒号表示一个选项 想要一个可选的参数;如果当前 ARGV 元素中有文本, 它在optarg
中返回,否则optarg
设置为零。...
...虽然你必须在字里行间做一些阅读。以下是您想要的:
#include <stdio.h>
#include <getopt.h>
int main(int argc, char* argv[] )
int getopt_ret;
int option_index;
static struct option long_options[] =
"praise", required_argument, 0, 'p'
, "blame", optional_argument, 0, 'b'
, 0, 0, 0, 0
;
while( -1 != ( getopt_ret = getopt_long( argc
, argv
, "p:b::"
, long_options
, &option_index) ) )
const char *tmp_optarg = optarg;
switch( getopt_ret )
case 0: break;
case 1:
// handle non-option arguments here if you put a `-`
// at the beginning of getopt_long's 3rd argument
break;
case 'p':
printf("Kudos to %s\n", optarg); break;
case 'b':
if( !optarg
&& NULL != argv[optind]
&& '-' != argv[optind][0] )
// This is what makes it work; if `optarg` isn't set
// and argv[optind] doesn't look like another option,
// then assume it's our parameter and overtly modify optind
// to compensate.
//
// I'm not terribly fond of how this is done in the getopt
// API, but if you look at the man page it documents the
// existence of `optarg`, `optind`, etc, and they're
// not marked const -- implying they expect and intend you
// to modify them if needed.
tmp_optarg = argv[optind++];
printf( "You suck" );
if (tmp_optarg)
printf (", %s!\n", tmp_optarg);
else
printf ("!\n");
break;
case '?':
printf("Unknown option\n");
break;
default:
printf( "Unknown: getopt_ret == %d\n", getopt_ret );
break;
return 0;
【讨论】:
这很好用,谢谢。不确定您从哪里获得 optindex;对我来说,它被称为 (extern int) optind。 第二个代码示例有错误,应该是optind
而不是optindex
。
看起来代码应该维护 optindex。否则,它将始终指向 0。我们需要为每个选项提前 optindex。【参考方案3】:
我最近自己也遇到了这个问题。我得出了与 Brian Vandenberg 和 Haystack 建议的类似的解决方案。但是为了提高可读性并避免代码重复,您可以将其全部包装在如下宏中:
#define OPTIONAL_ARGUMENT_IS_PRESENT \
((optarg == NULL && optind < argc && argv[optind][0] != '-') \
? (bool) (optarg = argv[optind++]) \
: (optarg != NULL))
宏可以这样使用:
case 'o': // option with optional argument
if (OPTIONAL_ARGUMENT_IS_PRESENT)
// Handle is present
else
// Handle is not present
break;
如果您有兴趣,可以在我写的博客文章中阅读有关此解决方案如何工作的更多信息: https://cfengine.com/blog/2021/optional-arguments-with-getopt-long/
此解决方案已经过测试,在撰写本文时,目前在 CFEngine 中使用。
【讨论】:
【参考方案4】:我也遇到了同样的问题,来到这里。然后我意识到了这一点。 您没有太多 "optional_argument" 的用例。如果需要选项,则从程序逻辑中检查,如果选项是可选的,则无需执行任何操作,因为在 getopt 级别,所有选项都是可选的,它们不是强制性的,因此没有“可选参数”的用例。希望这会有所帮助。
ps:对于上面的例子,我认为正确的选项是 --praise --praise-name "名字" --blame --blame-name "名字"
【讨论】:
以上是关于getopt 不将可选参数解析为参数的主要内容,如果未能解决你的问题,请参考以下文章