将 wordexp 的输出提供给 getopt_long 使我的 linux cli 应用程序崩溃
feeding the output of wordexp to getopt_long crashes my linux cli application
我正在尝试创建一个小型 linux 命令行应用程序。
可以通过在调用应用程序时传递参数来 运行 应用程序,我使用 getopt() 对其进行解析。
可以选择 运行 此应用程序处于交互模式,在这种情况下会显示一个小菜单,用户应该能够输入与他们在 运行ning 时添加的选项类似的选项应用程序。
为了避免在交互模式下创建不同的解析器,我想将键盘输入解析为 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]='[=12=]';
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
函数可以满足我的需要?
谢谢
将评论转移到答案——然后扩展想法。
- 在调用
main_dialog()
中的代码之前,您是否已经使用过getopt()
或getopt_long()
?如果是这样,您可能需要重新初始化它们,以便它们从当前参数列表的开头开始,而不是继续使用原始参数列表。
并不总是很清楚如何进行重置。
BSD(MacOSX)文档:
In order to use getopt()
to evaluate multiple sets of arguments, or to evaluate a single set of arguments multiple times, the variable optreset
must be set to 1 before the second and each additional set of calls to getopt()
, and the variable optind
must be reinitialized.
它还记录了额外的变量:extern int optreset;
其他系统没有清楚地记录需要做什么。请注意,getopt()
的 POSIX 规范明确指出(添加重点):
The variable optind
is the index of the next element of the argv[]
vector to be processed. It shall be initialized to 1 by the system, and getopt()
shall update it when it finishes with each element of argv[]
. If the application sets optind
to zero before calling getopt()
, the behavior is unspecified. When an element of argv[]
contains multiple option characters, it is unspecified how getopt()
determines which options have already been processed.
因此,您可能需要进行试验才能找到有效的方法。
从 来看,有时设置 optind = 0;
会起作用(这似乎适用于 Linux)。不清楚它是否适用于任何地方(POSIX 说它可能不会);使用前测试!
你可以尝试像这样的程序,我称之为 getopt-test.c
:
#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;
}
示例输出 (Mac OS X 10.10.4):
$ 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
$
注意处理 -ac
的 a
选项后的状态。唯一可见的状态变化在 optopt
中,修改 optopt
很可能不会触发重置。将 optind
设置为零可能会奏效,但程序启动时 optind
的官方值为 1
.
这也是为什么全局变量不好的一个实例。
另请注意,如果您在调用 getopt()
之间更改 argc
和 argv
的值,将会发生什么情况尚不清楚。规范并没有说每次的值都应该相同,但是如果您不将代码重置为起点,很可能会出现混乱。
我的代码的输出格式可以改进为将 optopt
打印为 2 位十六进制值。如果将 :
作为标记的一部分传递给转储函数可能会更好;那么第一个打印可能是:printf("%-.12s ", tag)
所以一切都对齐。 optarg
的值最有可能在长度上发生变化;这就是为什么它是最后一个。
我正在尝试创建一个小型 linux 命令行应用程序。 可以通过在调用应用程序时传递参数来 运行 应用程序,我使用 getopt() 对其进行解析。
可以选择 运行 此应用程序处于交互模式,在这种情况下会显示一个小菜单,用户应该能够输入与他们在 运行ning 时添加的选项类似的选项应用程序。
为了避免在交互模式下创建不同的解析器,我想将键盘输入解析为 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]='[=12=]';
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
函数可以满足我的需要?
谢谢
将评论转移到答案——然后扩展想法。
- 在调用
main_dialog()
中的代码之前,您是否已经使用过getopt()
或getopt_long()
?如果是这样,您可能需要重新初始化它们,以便它们从当前参数列表的开头开始,而不是继续使用原始参数列表。
并不总是很清楚如何进行重置。
BSD(MacOSX)文档:
In order to use
getopt()
to evaluate multiple sets of arguments, or to evaluate a single set of arguments multiple times, the variableoptreset
must be set to 1 before the second and each additional set of calls togetopt()
, and the variableoptind
must be reinitialized.
它还记录了额外的变量:extern int optreset;
其他系统没有清楚地记录需要做什么。请注意,getopt()
的 POSIX 规范明确指出(添加重点):
The variable
optind
is the index of the next element of theargv[]
vector to be processed. It shall be initialized to 1 by the system, andgetopt()
shall update it when it finishes with each element ofargv[]
. If the application setsoptind
to zero before callinggetopt()
, the behavior is unspecified. When an element ofargv[]
contains multiple option characters, it is unspecified howgetopt()
determines which options have already been processed.
因此,您可能需要进行试验才能找到有效的方法。
从 optind = 0;
会起作用(这似乎适用于 Linux)。不清楚它是否适用于任何地方(POSIX 说它可能不会);使用前测试!
你可以尝试像这样的程序,我称之为 getopt-test.c
:
#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;
}
示例输出 (Mac OS X 10.10.4):
$ 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
$
注意处理 -ac
的 a
选项后的状态。唯一可见的状态变化在 optopt
中,修改 optopt
很可能不会触发重置。将 optind
设置为零可能会奏效,但程序启动时 optind
的官方值为 1
.
这也是为什么全局变量不好的一个实例。
另请注意,如果您在调用 getopt()
之间更改 argc
和 argv
的值,将会发生什么情况尚不清楚。规范并没有说每次的值都应该相同,但是如果您不将代码重置为起点,很可能会出现混乱。
我的代码的输出格式可以改进为将 optopt
打印为 2 位十六进制值。如果将 :
作为标记的一部分传递给转储函数可能会更好;那么第一个打印可能是:printf("%-.12s ", tag)
所以一切都对齐。 optarg
的值最有可能在长度上发生变化;这就是为什么它是最后一个。