我正在尝试创建一个小型的linux命令行应用程序。 可以通过在调用应用程序时传递参数来运行应用程序,我使用getopt()解析它。
可以选择以交互模式运行此应用程序,在这种情况下会显示一个小菜单,用户应该能够输入与运行应用程序时添加的选项类似的选项。
为了避免在我处于交互模式时创建不同的解析器,我想将键盘输入解析为argc& argv并在同一个getopt()函数中提供它。
我创建的概念证明c
功能如下:
void menuDlg()
{
char kbinput[256];
wordexp_t we;
char **argAr;
int argCount = 0 ,i,er=0;
printf("showing menu options\n");
//...
//...
///grab keyboard input
fgets(kbinput,256,stdin);
///the "\n" in kbinput breaks we
///and we.we_wordc return weird value,
///so we need to remove it
for (i=0; i<256; i++)
{
if (kbinput[i]=='\n')
{
kbinput[i]='\0';
break;
}
}
printf("you typed |%s|\n",kbinput);
we.we_offs = 0;
if ( (er=wordexp(kbinput, &we, 0)) != 0)
{
printf("error in word expansion %d\n",er);
}
argAr = we.we_wordv;
argCount = we.we_wordc;
printf("we.c=%u\n",we.we_wordc);
main_dialog( argCount, argAr );
wordfree(&we);
}
是main_dialog()解析cli选项
void main_dialog( int argc, char* argv[] )
{
while ( (ch = getopt_long(argc, argv, "yscvh", longopts, NULL)) != -1 )
switch (ch)
{
...
}
}
但是当我运行这个时,我的应用程序在对getopt_long()
的调用中崩溃。
我读到argv
应该有null
的最终条目。所以对于参数“foo bar”,我们有argc = 2,但是argv [0] =“foo”,argv [1] =“bar”和argv [2] ='\ 0'。在wordexp documentation中,它几乎没有说明we_wordv的内部结构(这是关于一个拖尾的NULL数组条目),所以我不知道这是不是问题。
这可能是问题的根源吗?还有其他glib
函数可以完成我需要的工作吗?
谢谢
答案 0 :(得分:1)
转移评论以回答 - 然后扩展这些想法。
getopt()
中的代码之前,您是否已使用getopt_long()
或main_dialog()
?如果是这样,您可能需要重新初始化它们,以便它们从当前参数列表的开头开始,而不是继续使用原始参数列表。如何进行重置并不总是很清楚。
BSD(Mac OS X)文档:
为了使用
getopt()
来评估多组参数,或者多次评估一组参数,必须在第二组之前将变量optreset
设置为1,并且每组另外一组调用getopt()
,必须重新初始化变量optind
。
它还记录了额外的变量:extern int optreset;
其他系统没有明确记录需要完成的工作。请注意,getopt()
的POSIX规范明确指出(强调已添加):
变量
optind
是要处理的argv[]
向量的下一个元素的索引。它应由系统初始化为1,getopt()
在完成argv[]
的每个元素时应更新它。 如果应用程序在调用optind
之前将getopt()
设置为零,则行为未指定。当argv[]
的元素包含多个选项字符时,未指定{ {1}}确定已处理的选项。
因此,您可能需要尝试找出有效的方法。
从response comment判断,有时设置getopt()
将起作用(这似乎适用于Linux)。目前尚不清楚它是否会在任何地方发挥作用(POSIX说它可能不会);使用前测试!
您可以尝试像这样的程序,我称之为optind = 0;
:
getopt-test.c
示例输出(Mac OS X 10.10.4):
#include <stdio.h>
#include <unistd.h>
static void dump_getopt_state(const char *tag)
{
printf("%s:\n", tag);
printf("optind = %d, ", optind);
printf("opterr = %d, ", opterr);
printf("optopt = %d, ", optopt);
printf("optarg = %p\n", (void *)optarg);
}
int main(int argc, char **argv)
{
int opt;
dump_getopt_state("Initial");
char tag[32];
while ((opt = getopt(argc, argv, "ab:cd:")) != -1)
{
switch (opt)
{
case 'a':
case 'c':
sprintf(tag, "Option %c", opt);
break;
case 'b':
case 'd':
sprintf(tag, "Option %c", opt);
printf("Argument: %s\n", optarg);
break;
}
dump_getopt_state(tag);
}
dump_getopt_state("Final");
return 0;
}
在处理$ getopt-test
Initial:
optind = 1, opterr = 1, optopt = 0, optarg = 0x0
Final:
optind = 1, opterr = 1, optopt = 0, optarg = 0x0
$ getopt-test -a
Initial:
optind = 1, opterr = 1, optopt = 0, optarg = 0x0
Option a:
optind = 2, opterr = 1, optopt = 97, optarg = 0x0
Final:
optind = 2, opterr = 1, optopt = 97, optarg = 0x0
$ getopt-test -ac
Initial:
optind = 1, opterr = 1, optopt = 0, optarg = 0x0
Option a:
optind = 1, opterr = 1, optopt = 97, optarg = 0x0
Option c:
optind = 2, opterr = 1, optopt = 99, optarg = 0x0
Final:
optind = 2, opterr = 1, optopt = 99, optarg = 0x0
$
的{{1}}选项后注意状态。状态中唯一可见的更改位于a
,修改-ac
可能不会触发重置。将optopt
设置为零可能会有所帮助,但程序启动时optopt
的官方值为optind
。
这也是全局变量不好的对象课程。
另请注意,如果您在调用optind
之间更改1
和argc
的值,则不清楚会发生什么。规范并没有说每次的值都是相同的,但是如果你不将代码重置为起始点,则可能会出现混乱。
我的代码的输出格式可以改进,以打印argv
作为2位十六进制值。如果getopt()
作为标记的一部分传递给转储函数可能会更好;然后第一次打印可能是:optopt
所以一切都对齐。 :
的值最有可能在长度上变化; printf("%-.12s ", tag)
的值最大。这就是为什么它是最后一次。