xargparse.h 是命令行解析所需的头文件, xargparse.c 为主体函数实现
下面是一个简单且全面的使用示例
#include "xargparse.h"
int main(int argc, const char **argv) {
int integer;
char *str, *dest;
int src;
int *other_numbers;
argparse_option options[] = {
XBOX_ARG_BOOLEAN(NULL, "-h", "--help", "show help information", NULL, "help"),
XBOX_ARG_BOOLEAN(NULL, "-v", "--version", "show version", NULL, "version"),
XBOX_ARG_INT(&integer, "-i", "--input", "input file", " <NUMBER>", "input"),
XBOX_ARG_STR(&str, "-s", "--string", NULL, " <STRING>", "string"),
XBOX_ARG_STR_GROUP(&dest, NULL, NULL, "destination", NULL, "dest"),
XBOX_ARG_INT_GROUP(&src, NULL, NULL, "source", NULL, "src"),
XBOX_ARG_INTS_GROUP(&other_numbers, NULL, NULL, "catch the other number ...", NULL, "other-number"),
XBOX_ARG_END()
};
XBOX_argparse parser;
XBOX_argparse_init(&parser, options, 0);
XBOX_argparse_describe(&parser,
"main",
"\nA brief description of what the program does and how it works.",
"\nAdditional description of the program after the description of the arguments.");
XBOX_argparse_parse(&parser, argc, argv);
if (XBOX_ismatch(&parser, "help")) {
XBOX_argparse_info(&parser);
}
if (XBOX_ismatch(&parser, "version")) {
printf("v0.0.1\n");
}
if (XBOX_ismatch(&parser, "input")) {
printf("i = %d\n", integer);
}
if (XBOX_ismatch(&parser, "string")) {
printf("s = %s\n", str);
}
if (XBOX_ismatch(&parser, "dest")) {
printf("dest = %s\n", dest);
}
if (XBOX_ismatch(&parser, "src")) {
printf("src = %d\n", src);
}
int n = XBOX_ismatch(&parser, "other-number");
for (int i = 0; i < n; i++) {
printf("other number[%d] = %d\n", i, other_numbers[i]);
}
XBOX_free_argparse(&parser);
return 0;
}
您可直接拷贝此代码并编译运行, 也可以在本仓库下执行 make 以得到 ./examples/main
(base) kamilu@LZX:~/libc$ ./examples/main -h
Usage: main [dest] [src] [other-number]... [-h --help][-v --version]
[-i <NUMBER> --input <NUMBER>]
[-s <STRING> --string <STRING>]
Description:
A brief description of what the program does and how it works.
-h --help show help information
-v --version show version
-i <NUMBER> --input <NUMBER> input file
-s <STRING> --string <STRING>
Additional description of the program after the description of the arguments.
(base) kamilu@LZX:~/libc$ ./examples/main -v
v0.0.1
(base) kamilu@LZX:~/libc$ ./examples/main -i
[Args Parse Error]: option [--input] needs one argument
(base) kamilu@LZX:~/libc$ ./examples/main -i 100
i = 100
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200
i = 100
s = 200
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200 300
i = 100
s = 200
dest = 300
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200 300 400
i = 100
s = 200
dest = 300
src = 400
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200 300 "400"
i = 100
s = 200
dest = 300
src = 400
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200 "300" "400"
i = 100
s = 200
dest = 300
src = 400
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200 "300" "400" "500"
i = 100
s = 200
dest = 300
src = 400
other number[0] = 500
(base) kamilu@LZX:~/libc$ ./examples/main -i 100 -s 200 "300" "400" abc
[Args Parse Error]: argument assign to be int but get [abc]
以上面的示例为例, 我们需要先定义变量然后构建一个 options 并与变量绑定
当然方便起见也可以直接全局变量, 或者与自定义参数结构体内部的字段绑定
int i;
char *s, *dest;
int src;
int *other_numbers;
argparse_option options[] = {
XBOX_ARG_BOOLEAN(NULL, "-h", "--help", "show help information", NULL, "help"),
XBOX_ARG_BOOLEAN(NULL, "-v", "--version", "show version", NULL, "version"),
XBOX_ARG_INT(&integer, "-i", "--input", "input file", " <NUMBER>", "input"),
XBOX_ARG_STR(&str, "-s", "--string", NULL, " <STRING>", "string"),
XBOX_ARG_STR_GROUP(&dest, NULL, NULL, "destination", NULL, "dest"),
XBOX_ARG_INT_GROUP(&src, NULL, NULL, "source", NULL, "src"),
XBOX_ARG_INTS_GROUP(&other_numbers, NULL, NULL, "catch the other number ...", NULL, "other-number"),
XBOX_ARG_END()
};
以 XBOX_ARG*
开头的是宏, 第一个位置用于参数绑定, 后面的内容会被展开为字符串用于后续的解析
xargparse.h 一共提供了 8 种宏
-h
, 后面不需要跟其他参数信息-i 100
-s "hello world"
-s nihao
组即不需要 -i
--input
指定接收的正常参数, 以上面的例子为例, 如果命令行传参为: ./examples/main abc 100
, 其中 "abc" 会被 dest 组接收, 100 会被 src 组接收, 需要注意的是组是有前后顺序的
上述参数宏分别对应不同的使用场景, 您应该合理的使用和传参, 第一个位置需要绑定的参数类型与宏对应
int
对应 XBOX_ARG_INT 和 XBOX_ARG_INT_GROUPchar*
对应 XBOX_ARG_STR 和 XBOX_ARG_STR_GROUPint*
对应 XBOX_ARG_INTS_GROUPchar **
对应 XBOX_ARG_STRS_GROUP上述的宏有6个参数位置, 分别是 (bind, short_name, long_name, help_info, append_info, name)
如果不需要则传 NULL 即可
-h
-v
-s
--h
不合法, --info
--space
--config
合法-O<number>
, 则添加 "<number>"
即可其中需要注意如下几点
a-z_-
的组合, 如果您希望使用其他字符你可手动修改其中 check_valid_character
函数之后您只需要构建对象, 初始化并解析即可
XBOX_argparse parser;
XBOX_argparse_init(&parser, options, 0);
XBOX_argparse_describe(&parser, "main", "\ndescription", "\naaa.");
XBOX_argparse_parse(&parser, argc, argv);
其中有几点需要说明:
XBOX_argparse_describe
的2 3 4位置的参数分别为 解析器的名字, 简要描述, 结尾描述
XBOX_argparse_init
第三个参数为 flag 标记位, 用于控制解析时的选项, 默认传 0 即可, 具体用法会在下文 解析扩展选项 中介绍
上例中的每一个选项中都使用了 NAME, 这并不是一个必选项, 除了使用 XBOX_is_match 来判断是否接收到了参数, 也可以判断绑定的参数的值.
XBOX_ismatch
函数用于判断是否接收到了参数, 并返回匹配的个数. 第二个参数是您定义的名字或长参数的名字
int XBOX_ismatch(XBOX_argparse *parser, char *name);
int XBOX_match_pos(XBOX_argparse *parser, char *name);
如果您使用的是若干参数的组, 您可以通过接收其返回值获取,如下所示
if (XBOX_ismatch(&parser, "help")) {
XBOX_argparse_info(&parser);
}
if (XBOX_ismatch(&parser, "dest")) {
printf("dest = %s\n", dest);
}
if (XBOX_ismatch(&parser, "src")) {
printf("src = %d\n", src);
}
int n = XBOX_ismatch(&parser, "other-number");
for (int i = 0; i < n; i++) {
printf("other number[%d] = %d\n", i, other_numbers[i]);
}
XBOX_free_argparse(&parser);
XBOX_match_pos
用于判断接收到的参数的匹配位置
./main 100 -i 200
| | |
1 2 3
./main -abc -d 100
||| | |
123 4 5
xbox 提供了一个函数 XBOX_argparse_info
用于帮助信息的输出, 建议您在 options 中添加 -h 选项并将其绑定到 XBOX_argparse_info 函数, 用于辅助帮助信息的输出, 当然您也可以编写您自定义的 help 信息文档. 默认的帮助信息会自动为您实现对齐和说明
(base) kamilu@LZX:~/libc$ ./examples/main --help
Usage: main [dest] [src] [other-number]... [-h --help][-v --version]
[-i <NUMBER> --input <NUMBER>]
[-s <STRING> --string <STRING>]
Description:
A brief description of what the program does and how it works.
-h --help show help information
-v --version show version
-i <NUMBER> --input <NUMBER> input file
-s <STRING> --string <STRING>
Additional description of the program after the description of the arguments.
xargparse 使用了动态内存分配, 所以最后请注意使用 XBOX_free_argparse
释放内存
接下来您利用这些参数去处理您程序真正想要完成的事情了
如果您希望扩展解析时选项, 您可修改 flag 标记位, 使用 |
运算将他们组合传入 flag
-O1 -Iinclude/
-i=123
curl -Ls xxx
, 此选项仅支持 BOOLEAN 类型的粘连传递, 请不要加入其他类型XBOX_ARGPARSE_IGNORE_UNKNOWN
: 正常来说如果传入未定义过的参数会报如下错误
(base) kamilu@LZX:~/libc$ ./examples/main -w
[Args Parse Error]: no match options for [-w]
在 flag 中加入此选项后不会报错, 只会单纯的忽略未知参数
XBOX_argparse_init(&parser, options, XBOX_ARGPARSE_IGNORE_UNKNOWN);
XBOX_ARGPARSE_ENABLE_STICK
: 允许参数与值粘连
正常情况下会被认为是粘连参数
$ ./examples/main -i100
[Args Parse Error]: no match options for [-i100]
需要修改 flag 并重新编译
XBOX_argparse_init(&parser, options, XBOX_ARGPARSE_ENABLE_STICK);
$ ./examples/main -i100 -sabcd
i = 100
s = abcd
XBOX_ARGPARSE_ENABLE_EQUAL
: 允许等号赋值
XBOX_argparse_init(&parser, options, XBOX_ARGPARSE_ENABLE_EQUAL);
$ ./examples/main -s="hello world"
s = hello world
$ ./examples/main -s=hello
s = hello
允许参数与值粘连与允许等号赋值参数赋值通常一起使用, 并优先考虑 =
赋值, 即对于 ./examples/main -s="hello world"
来说, 结果是 s = hello world
而不是 s = =hello world
XBOX_argparse_init(&parser, options, XBOX_ARGPARSE_ENABLE_EQUAL|XBOX_ARGPARSE_ENABLE_STICK);
XBOX_ARGPARSE_ENABLE_ARG_STICK
: 支持bool类型的参数粘连, 注意与 XBOX_ARGPARSE_ENABLE_STICK
(选项与参数粘连) 不同
$ ./examples/main -hv
[Args Parse Error]: Boolean argument [-h] is sticky in [-hv], do you mean XBOX_ARGPARSE_ENABLE_ARG_STICK?
XBOX_argparse_init(&parser, options, XBOX_ARGPARSE_ENABLE_ARG_STICK);
$ ./examples/main -hv
Usage: main [dest] [src] [other-number]... [-h --help][-v --version]
[-i <NUMBER> --input <NUMBER>]
[-s <STRING> --string <STRING>]
Description:
A brief description of what the program does and how it works.
-h --help show help information
-v --version show version
-i <NUMBER> --input <NUMBER> input file
-s <STRING> --string <STRING>
Additional description of the program after the description of the arguments.
v0.0.1
您可复制如下代码进行编译, 也可以直接运行编译得到的 kcc 可执行文件
#include "xargparse.h"
char *output = NULL;
char **include_path = NULL;
char **files = NULL;
char *optimize = NULL;
char **warning = NULL;
char **library_path = NULL;
char **library_name = NULL;
char *c_standard = NULL;
int debug = 0;
int main(int argc, const char **argv) {
argparse_option options[] = {
XBOX_ARG_BOOLEAN(NULL, "-c", NULL, "Compile and assemble, but do not link.", NULL, "compile"),
XBOX_ARG_STR(&output, "-o", NULL, "Place the output into <file>.", " <file>", NULL),
XBOX_ARG_STRS(&include_path, "-I", NULL, "Add <dir> to the end of the main include path.", "<dir>", "include"),
XBOX_ARG_STRS(
&library_path, "-L", NULL, "Add <dir> to the end of the main library path.", "<dir>", "library_path"),
XBOX_ARG_STRS(&library_name, "-l", NULL, "Search <lib> in library path", "<lib>", "library_name"),
XBOX_ARG_BOOLEAN(&debug, "-g", NULL, "Generate debug information in default format.", NULL, "debug"),
XBOX_ARG_STRS(&warning, "-W", NULL, "Warning information option", NULL, NULL),
XBOX_ARG_STR(&optimize, "-O", "--optimize", "optimization level to <number>.", "<number>", NULL),
XBOX_ARG_STR(&c_standard, NULL, "--std", "C Compile standard, supported: {c99}", "=<standard>", NULL),
XBOX_ARG_BOOLEAN(NULL, "-h", "--help", "show help information", NULL, "help"),
XBOX_ARG_BOOLEAN(NULL, "-v", "--version", "show version", NULL, "version"),
XBOX_ARG_STRS_GROUP(&files, NULL, NULL, NULL, NULL, "src"),
XBOX_ARG_END()};
XBOX_argparse parser;
XBOX_argparse_init(
&parser, options, XBOX_ARGPARSE_ENABLE_ARG_STICK | XBOX_ARGPARSE_ENABLE_STICK | XBOX_ARGPARSE_ENABLE_EQUAL);
XBOX_argparse_describe(&parser,
"kcc",
"Kamilu's C Compiler",
"document: <https://github.com/luzhixing12345/kcc>\nbug report: "
"<https://github.com/luzhixing12345/kcc/issues>");
XBOX_argparse_parse(&parser, argc, argv);
if (XBOX_ismatch(&parser, "help")) {
XBOX_argparse_info(&parser);
XBOX_free_argparse(&parser);
return 0;
}
if (XBOX_ismatch(&parser, "version")) {
printf("%s\n", XBOX_VERSION);
XBOX_free_argparse(&parser);
return 0;
}
if (XBOX_ismatch(&parser, "compile")) {
printf("use -c\n");
}
if (debug) {
printf("use -g\n");
}
int n = XBOX_ismatch(&parser, "src");
for (int i = 0; i < n; i++) {
printf("file[%d] = [%s]\n", i, files[i]);
}
n = XBOX_ismatch(&parser, "include");
for (int i = 0; i < n; i++) {
printf("include path[%d] = [%s]\n", i, include_path[i]);
}
n = XBOX_ismatch(&parser, "library_path");
for (int i = 0; i < n; i++) {
printf("library path[%d] = [%s]\n", i, library_path[i]);
}
n = XBOX_ismatch(&parser, "library_name");
for (int i = 0; i < n; i++) {
printf("library name[%d] = [%s]\n", i, library_name[i]);
}
if (c_standard) {
printf("C standard = [%s]\n", c_standard);
}
if (optimize) {
printf("optimize = [%s]\n", optimize);
}
if (output) {
printf("output file = [%s]\n", output);
}
XBOX_free_argparse(&parser);
return 0;
}
kcc 提供了与 gcc 类似的参数传递方式
(base) kamilu@LZX:~/libc$ ./examples/kcc a.c -o a
file[0] = [a.c]
output file = [a]
(base) kamilu@LZX:~/libc$ ./examples/kcc a.c -I include1 -Iinclude2 -I=include3 -o a
file[0] = [a.c]
include path[0] = [include1]
include path[1] = [include2]
include path[2] = [include3]
output file = [a]
(base) kamilu@LZX:~/libc$ ./examples/kcc a.c -I include1 -Iinclude2 -I=include3 -labc -l=ccc -o a
file[0] = [a.c]
include path[0] = [include1]
include path[1] = [include2]
include path[2] = [include3]
library name[0] = [abc]
library name[1] = [ccc]
output file = [a]
这对于一些 GNU 工具的实现来说很有效
$ ./examples/main 123 abc
[Args Parse Error]: argument assign to be int but get [abc]
$ ./examples/main 123 123a1
[Args Parse Error]: argument assign to be int but get [123a1]
$ ./examples/main 123 -s
[Args Parse Error]: option [--string] needs one argument