到目前为止,大部分人编写的所有程序都可以用一个命令运行。举个例子,如果我们编译了一个称之为 myprog
的可执行程序,我们能够在 Linux 命令行使用以下命令运行它:
./myprog
但是,如果你想从命令行向正在运行的程序传递信息,该怎么办?考虑一个更复杂的程序,比如 GCC。要编译 myprog
的可执行文件,我们在命令行输入以下内容:
gcc -o myprog myprog.c
字符串 -o
, myprog
和 myprog.c
都是 gcc 的命令行参数。(从技术上讲,gcc
也是一个参数,我们稍后会看到)
命令行参数非常有用。毕竟,如果不能向 C 函数传递参数,C 函数就不会很有用了——添加向程序传递参数的功能会使它们更有用。事实上,你在命令行上传递的所有参数最终都作为程序中 main
主函数的参数。
到目前为止,我们用于 C 程序的框架大致如下:
#include
int main()
{
return 0;
}
从现在开始,我们的示例可能看起来更像这样:
#include
int main(int argc, char *argv[])
{
return 0;
}
正如你所看到的,main
函数现在有了参数。变量 argc
代表“参数数量”;argc
包含传递给程序的参数个数。变量 argv
代表“参数向量”。向量(vector)是一维数组,argv
是字符串的一维数组。每个字符串都是传递给程序的参数之一。
例如命令行:
gcc -o myprog myprog.c
将产生 GCC 内部的以下值:
argument | value |
---|---|
argc |
4 |
argv[0] |
gcc |
argv[1] |
-o |
argv[2] |
myprog |
argv[3] |
myprog.c |
如你所见,第一个参数(argv[0]
)是调用程序的名称,在本示例中它是 gcc
。因此,程序将始终至少有一个参数,argc
将始终至少为 1
。
以下程序接受任意数量的命令行参数并将其打印出来:
#include
int main(int argc, char const *argv[]) {
printf("argc: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
如果你编译你的程序名为 myprog
,然后以 ./myprog a b c
的方式调用它,则它会输出以下的打印:
user@hostname:examples$ ./myprog a b c
argc: 4
argv[0] = ./myprog
argv[1] = a
argv[2] = b
argv[3] = c
使用自己的程序直接从 argv
向量中提取选项是最容易的,尽管很乏味。
使用标准 C 选项处理函数 getopt
或同一函数的 GNU 增强版本 getopt_long
稍微不那么乏味,它允许 GNU-style 的长选项(例如,--quiet
而不是 -q
)。
POSIX (Portable Operating System Interface) 标准,建议使用以下命令行参数约定。
-
)开头,则为选项。-abc
和 -a -b -c
是相同的。gcc
命令 -o
选项需要输出文件名。-o fname
和 -ofname
是相同的。--
终止所有选项;后面的所有命令行参数都被视为非选项参数,即使它们以连字符开头。此外,GNU 还添加了长选项,如 --help
和 --version
选项。一个长选项以 --
开头,然后后面跟一串字母数字字符和连字符。选项名称通常为一到三个单词,中间用连字符分隔单词。用户可以缩写选项名称,只要缩写是唯一的。长选项(如 --verbose
)通常具有短选项同义词(如 -v
)。
可以为长选项指定参数,如下所示:
--option-name=value
不能在选项名称和等号之间或等号和选项值之间键入空格。
--option-name= value # No
--option-name =value # No
--option-anme = value # No
对于程序的命令行选项,最好遵循 POSIX 指南。最简单的办法就是使用 GNU-style 的 getopt_long
函数解析它们。长名称选项的优点之一是它们可以在不同程序之间保持一致。例如,用户期待任何一个程序都有一个 “help” 的选项,帮助他们了解程序的基本用法。
接下来我们了解一下 getopt_long
函数的用法:
#include
int getopt_long (int argc, char *const argv[],
const char *shortopts,
const struct option *longopts, int *longind);
第 1 个与第 2 个参数 argc、argv 与 main
函数一致。
第 3 个参数表示短选项字符串,可以是下列元素:
:
表示该选项后必须跟一个参数,参数紧跟在选项后或者以空格隔开。该参数的指针赋给 optarg
。::
表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给 optarg
。(这个特性是 GNU 的扩展)第 4 个参数指定长选项的结构体 struct option
,结构体定义如下:
struct option
{
const char *name;
int has_arg;
int *flag;
int val;
};
name: 长选项的名称。
has_arg: 表示选项后面是否携带参数,可以使用 no_argument
, required_argument
及 optional_argument
三个值:
no_argument
选项后面不跟参数,如:--version
, --help
required_argument
选项需要一个参数,输入格式为:--option name
或 --option=name
optional_argument
选项采用可选参数,输入格式为:--option
或 --option=name
在头文件
中这三个值的定义如下:
/* Names for the values of the 'has_arg' field of 'struct option'. */
#define no_argument 0
#define required_argument 1
#define optional_argument 2
flag: 该字段有两种含义,空或者非空:
val: 表示指定函数找到该选项时的返回值。
第 5 个参数 longind
参数一般赋为 NULL 即可;如果没有设置为 NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在 longopts 中的索引值,这可以用于错误诊断。
以下代码演示了 getopt_long
的基本用法:
hello.c
#include // included for `printf`
#include // included for `getopt_long`
#include // included for `basename`
#include // included for `EXIT_SUCCESS|EXIT_FAILURE`
#define VERSION "1.0.0"
// define option table
static const struct option longopts[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
// TODO::
{"greeting", required_argument, NULL, 'g'},
{"next-generation", no_argument, NULL, 'n'},
{"traditional", no_argument, NULL, 't'},
// ----------
{NULL, 0, NULL, 0}};
// Print help info.
static void print_help(const char *progname) {
printf("Usage: %s [OPTION]...\n", progname);
printf("Options:\n");
printf(" -h, --help display this help\n");
printf(" -V, --version display version information\n");
// TODO::
printf(" -g, --greeting=TEXT use TEXT as the greeting message\n");
printf(" -t, --traditional use traditional greeting format\n");
printf(" -n, --next-generation use next-generation greeting format\n");
// ----------
}
static void print_version() {
printf("%s\n", VERSION);
}
int main(int argc, char *argv[])
{
int optc;
const char *program_name = basename(argv[0]);
int lose = 0;
// TODO::
int t = 0, n = 0;
const char *greeting = NULL;
// ----------
while ((optc = getopt_long(argc, argv, "hVg:nt", longopts, NULL)) != -1)
switch (optc) {
/* One goal here is having --help and --version exit immediately,
per GNU coding standards. */
case 'h':
print_help(program_name);
exit(EXIT_SUCCESS);
break;
case 'V':
print_version();
exit(EXIT_SUCCESS);
break;
case 'g':
greeting = optarg;
break;
case 'n':
n = 1;
break;
case 't':
t = 1;
break;
default:
lose = 1;
break;
}
if (lose || optind < argc) {
/* Print error message and exit. */
if (optind < argc)
fprintf(stderr, "%s: extra operand: %s\n", program_name,
argv[optind]);
fprintf(stderr, "Try `%s --help' for more information.\n",
program_name);
exit(EXIT_FAILURE);
}
if (t) {
printf("hello, world\n");
} else if (n) {
printf("+---------------+\n");
printf("| Hello, world! |\n");
printf("+---------------+\n");
} else {
if (!greeting) greeting = "Hello, world!";
puts(greeting);
}
return 0;
}
定义长选项 struct option
结构体数组 longopts:
调用 getopt_long
函数解析命令行参数
遵循 GNU Coding Standards 规范,提供 --version
和 --help
两个标准选项
延展阅读
△ \triangle △ C/C++读写二进制文件
▽ \bigtriangledown ▽ Linux C/C++ 单实例进程设计