将wordexp的输出提供给getopt_long会导致我的linux cli应用程序崩溃

时间:2015-08-06 17:49:58

标签: c linux arguments glib

我正在尝试创建一个小型的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函数可以完成我需要的工作吗?

谢谢

1 个答案:

答案 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之间更改1argc的值,则不清楚会发生什么。规范并没有说每次的值都是相同的,但是如果你不将代码重置为起始点,则可能会出现混乱。

我的代码的输出格式可以改进,以打印argv作为2位十六进制值。如果getopt()作为标记的一部分传递给转储函数可能会更好;然后第一次打印可能是:optopt所以一切都对齐。 :的值最有可能在长度上变化; printf("%-.12s ", tag)的值最大。这就是为什么它是最后一次。