简介
命令行参数是在程序运行时由操作系统传递给程序的,当程序被另一个程序请求时,例如像这样的命令行解释器(“shell”)
cmd.exe在Windows上,或者
bash在Linux和OS X上。用户键入一个命令,shell调用操作系统来运行程序。这具体是如何实现的超出了本文的范围(在Windows上,请查阅
CreateProcess;在UNIX和类UNIX系统上,请查阅
fork(3)和
exec(3)手册)。
命令行参数的用途多种多样,但最主要的两个是
- 修改程序行为 - 命令行参数可用于告诉程序您期望它如何工作;例如,某些程序有一个-q(静默)选项,用于告诉它们不要输出太多文本。
- 让程序在没有用户交互的情况下运行 - 这对于从脚本或其他程序调用的程序特别有用。
命令行
向程序添加解析命令行参数的功能非常简单。每个C和C++程序都有一个
main函数。在不具备解析命令行能力的程序中,
main通常定义如下
要查看命令行,我们必须为main函数添加两个参数,按照惯例,它们分别命名为
argc(
argument
count,参数数量)和
argv(
argument
vector,参数向量【此处,vector指数组,而非C++或欧几里得向量】)。
argc的类型是
int和
argv通常的类型是
char**或
char* [](见下文)不同。
main现在看起来像这样
|
int main(int argc, char* argv[]) // or char** argv
|
argc告诉您有多少个命令行参数。它至少是1,因为第一个字符串
argv (
argv[0]是调用程序的命令。
argv包含实际的命令行参数作为一个字符串数组,其中第一个(正如我们已经发现的)是程序的名称。尝试这个例子
1 2 3 4 5 6 7
|
#include <iostream>
int main(int argc, char* argv[])
{
std::cout << argv[0] << std::endl;
return 0;
}
|
此程序将打印您用于运行它的命令的名称:如果您调用可执行文件“a.exe”(Windows)或“a.out”(UNIX),它可能会分别打印“a.exe”或“./a.out”(如果您从shell运行它)。
前面提到
argc包含传递给程序的参数数量。这很有用,因为它可以告诉我们在用户未传递正确数量的参数时,然后我们可以告知用户如何运行我们的程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#include <iostream>
int main(int argc, char* argv[])
{
// Check the number of parameters
if (argc < 2) {
// Tell the user how to run the program
std::cerr << "Usage: " << argv[0] << " NAME" << std::endl;
/* "Usage messages" are a conventional way of telling the user
* how to run a program if they enter the command incorrectly.
*/
return 1;
}
// Print the user's name:
std::cout << argv[0] << "says hello, " << argv[1] << "!" << std::endl;
return 0;
}
|
示例输出(未传递参数)
用法:a.exe <姓名>
示例输出(传递了一个参数)
a.exe 向 Chris 问好!
参数和选项
参数(Arguments)和选项(Parameters)是传递给程序的字符串,用于向程序提供信息。例如,一个用于移动文件的程序可以带两个参数——源文件和目标文件
move /path/to/source /path/to/destination(注意:在Windows上,这些路径将使用反斜杠【并且可能有一个驱动器前缀,如
C】,然而,由于Windows支持路径中的反斜杠和正斜杠,而UNIX系统仅支持正斜杠,因此本文将始终使用正斜杠)。
在此示例中,程序将如下所示
1 2 3 4 5 6 7 8 9 10 11
|
#include <iostream>
int main(int argc, char* argv[])
{
if (argc < 3) { // We expect 3 arguments: the program name, the source path and the destination path
std::cerr << "Usage: " << argv[0] << "SOURCE DESTINATION" << std::endl;
return 1;
}
return move(argv[1], argv[2]); // Implementation of the move function is platform dependent
// and beyond the scope of this article, so it is left out.
}
|
如果我们想允许使用多个源路径,我们可以使用循环和std::vector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char* argv[])
{
if (argc < 3) { // We expect 3 arguments: the program name, the source path and the destination path
std::cerr << "Usage: " << argv[0] << "SOURCE DESTINATION" << std::endl;
return 1;
}
std::vector <std::string> sources;
std::string destination;
for (int i = 1; i < argc; ++i) { // Remember argv[0] is the path to the program, we want from argv[1] onwards
if (i + 1 < argc)
sources.push_back(argv[i]); // Add all but the last argument to the vector.
else
destination = argv[i];
}
return move(sources, destination);
|
参数可以作为选项的值传递。选项通常在UNIX上以单个连字符(-)表示“短选项”,或以双连字符(--)表示“长选项”,或在Windows上以正斜杠表示。本文将使用连字符(单双连字符)。继续以
move程序为例,该程序可以使用
-d/
--destination选项来告诉它哪个路径是源文件,哪个是目标文件,如
move -d /path/to/destination /path/to/source和
move --destination /path/to/destination /path/to/source。选项总是右结合的,这意味着选项的参数始终是紧跟其右边的文本。
让我们扩展之前的示例以使用目标选项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char* argv[])
{
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << "--destination DESTINATION SOURCE" << std::endl;
return 1;
}
std::vector <std::string> sources;
std::string destination;
for (int i = 1; i < argc; ++i) {
if (std::string(argv[i]) == "--destination") {
if (i + 1 < argc) { // Make sure we aren't at the end of argv!
destination = argv[i++]; // Increment 'i' so we don't get the argument as the next argv[i].
} else { // Uh-oh, there was no argument to the destination option.
std::cerr << "--destination option requires one argument." << std::endl;
return 1;
}
} else {
sources.push_back(argv[i]);
}
}
return move(sources, destination);
}
|
现在参数可以按任意顺序排列,只要目标路径紧跟在“--destination”的右侧。
更多关于用法消息
我们的用法消息很有帮助,但如果我们必须从多个地方打印它,我们就必须复制代码。显然,解决这个问题的方法是使用函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
#include <iostream>
#include <string>
#include <vector>
static void show_usage(std::string name)
{
std::cerr << "Usage: " << argv[0] << " <option(s)> SOURCES"
<< "Options:\n"
<< "\t-h,--help\t\tShow this help message\n"
<< "\t-d,--destination DESTINATION\tSpecify the destination path"
<< std::endl;
}
int main(int argc, char* argv[])
{
if (argc < 3) {
show_usage(argv[0]);
return 1;
}
std::vector <std::string> sources;
std::string destination;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if ((arg == "-h") || (arg == "--help")) {
show_usage(argv[0]);
return 0;
} else if ((arg == "-d") || (arg == "--destination")) {
if (i + 1 < argc) { // Make sure we aren't at the end of argv!
destination = argv[i++]; // Increment 'i' so we don't get the argument as the next argv[i].
} else { // Uh-oh, there was no argument to the destination option.
std::cerr << "--destination option requires one argument." << std::endl;
return 1;
}
} else {
sources.push_back(argv[i]);
}
}
return move(sources, destination);
}
|
现在,用户无需猜测,就可以使用
-h或
--help选项来调用我们的程序,以了解如何运行该命令。
Getopt
这些查找命令行参数的方法简单且不太健壮。查找选项的最佳方法是使用 **getopt** 系列函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
#include <getopt.h>
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
int getopt_long_only(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
|
(来自
手册页)
手册页中有如何使用它们的示例。