簡介
命令列引數是在程式執行時由作業系統傳遞給程式的,當程式被另一個程式請求時,例如像這樣的命令列直譯器(“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);
|
(來自
手冊頁)
手冊頁中有如何使用它們的示例。