http://blog.csdn.net/luckheadline
一. 命令行規範
關於實現命令行參數, C 語言有着由來已久的傳統規範。
1. 可選命令行參數前有一個“ - ”, DOS 或 WINDOWS 系統也可以用“ / ”。
2. 多個可選命令行參數之間沒有順序要求,每個參數都可以放置在參數列表中的任何位置。
3. 可選命令行參數本身可能會帶有參數。
4. 可能有些命令行程序並不遵從以上的規範,雖然這樣做並不推薦。
二. 如何實現命令行參數處理(以 C 語言爲例)
命令行參數總是表現爲一個字符串數組,這個字符串數組被稱爲 ”argv”( 代表 arguments values), 還有一個整數 argc( 代表 arguments count) 用來表示命令行參數列表中參數的個數。因此,我們的 main 函數的格式如下:
int main(int argc, char* argv[]), 務必要記住 argv[0] 是程序的名字,在 usage 聲明中你可能會用到它。 main 函數中的第一任務是實現可選參數,操作 argv 的標準方式如下所示:
- int i;
- int quiet=0; //Value for the “-q” optional argument.
- for (i = 1; i < argc; i ++) //skip argv[0].
- {
- /*
- *Use the ‘strcmp’ function to compare the argv values to a string
- *of your choice(here, it’s a *optinal argument “-q”). When
- *strcmp returns 0, it means that the two strings are identical.
- */
- if (strcmp(argv[i], “-q”) == 0) //Process optional arguments
- {
- quiet = 1; //This is used as a Boolean value.
- }
- else
- {
- //Process non-optional arguments here.
- }
- }
請注意,正如這段代碼所示,可選參數“
-q ”可以被放置於參數列表的任何位置而且程序都可以正常 work 。 如果可選參數本身還有參數的話,那麼代碼將更加的 trickier :
- int i;
- int opt = 0;
- int optarg1 = 0;
- int optarg2 = 0;
- for (i = 1; i < argc; i ++)
- {
- if (strcmp(argv[i], “-opt”) == 0)
- {
- opt = 1; //This is used as a boolean value.
- /*
- *The last argument is argv[argc -1]. Make sure there
- *are enough arguments.
- */
- if (i + 2 <= argc - 1) //There are enough arguments in argv.
- {
- /*
- *Increment ‘i’ twice so that you don’t check
- *these arguments the next time through the loop.
- */
- i ++;
- optarg1 = atoi(argv[i]); //Convert string to int
- i ++;
- optarg2 = atoi(argv[i]);
- }
- else
- {
- //Print usage statement and exit.
- }
- }
- else
- {
- //Process non-optional arguments here.
- }
- }
在很多情況下,命令行處理可能會非常麻煩,不過上面的代碼已經可以應付相當多的情況了。
BTW, 在 Linux 下,習慣上會有兩種形式的命令行參數,一種是短形式,通常由 ”-” 加一個字母組成,這種形式的好處是便捷;另一種是長形式,通常由 ”--” 加一個單詞組成,這種形式的好處是形象,好記,直觀。
三. Usage 聲明
有時候,用戶可能會輸入不合法的命令行參數,你的程序需要處理這種情況以避免程序 crash 。這種情況的正確處理方式是:
1. 顯示 usage 聲明
2. 退出程序
例如,假設你的程序除了程序名還期望有 3 個參數,並且要接受另外一個可選參數:
usage 聲明的以下幾點意見:
1. usage 信息 : 總是使用“ usage ”開頭,接着是程序名和參數名。參數名應該儘可能的指明參數的意義,比如上面的 filename 。參數名不應該包含空格!可選參數使用 [] ,比如上面程序中的 ”-w” 。非可選參數不要使用 [] !總是打印 stderr ,而非 stdout ,以指明程序沒有正確 invoke 。
2. 程序名:總是使用 argv[0] 以指明程序名而非顯式的指出。因爲這樣,當你重新命名程序之後,不用重寫代碼。
3. 退出程序:使用 exit 函數( exit 函數被定義在頭文件 <stdlib.h> )。 exit 函數的任何非零參數都標誌着程序的非正常結束( exit ( 0 )標識程序的正常執行,但你很少會像這樣用)。你也可以使用宏 EXIT_FAILURE 和 EXIT_SUCCESS 以取代 1 和 0 作爲 exit 函數的參數。
如果不得不多次寫 usage 聲明,你可以將它分離出來作爲一個獨立的函數,並且將程序名( argv[0] )作爲參數傳給它。那樣它就可以被 main 函數在任何需要的時候調用了。
四. 常見錯誤
1. 當程序接收到不正確參數時,應該總是打印一個 usage 消息給 stderr 。如果處理失敗將會自動 redo 。
2. 不要假設可選參數會固定放在參數列表的某個位置上。
3. 如果不方便的話,就不要嘗試一遍處理所有的命令行參數。
4. 不要修改 argv 數組。
五. getOpt() 的使用
getOpt() 是一個專門設計來減輕命令行處理負擔的庫函數。它位於 unistd.h 系統頭文件中,其原型如下:
int getopt(int argc, char *const argv[], const char *optstring);
函數給定了命令參數的數量 (argc) ,指向這些參數的數組 (argv) 和選項字符串 (optstring) 後, getopt() 將返回第一個選項,並設置一些全局變量。使用相同的參數再次調用該函數時,它將返回下一個選項,並設置相應的全局變量。如果不再有識別到的選項,將返回 -1 ,此任務就完成了。
getopt() 所設置的全局變量包括:
optarg :指向當前選項參數(如果有)的指針。
optind :再次調用 getopt() 時的下一個 argv 指針的索引。
optopt :最後一個已知選項。
可以重複調用 getopt() ,直到其返回 -1 爲止;任何剩下的命令行參數通常視爲文件名或稱程序相應的其他內容。見以下代碼示例:
Unix 程序還支持長選項,可以通過使用 getopt_long() 向程序添加長選項支持,因爲 getopt_long() 是同時支持長選項和短選項的 getopt() 版本。 getopt_long() 函數還接受其他參數,其中一個是指向 struct option 對象數組的指針。結構如下:
成員是指向長選項名稱(帶兩個短橫線)的指針。 has_arg 成員設置爲 no_argument, optional_argument, 或 required_argument (均在 getopt.h 中定義)之一,以指示選項是否具有參數。如果 flag 成員未設置爲 NULL ,在處理期間遇到此選項時,會使用 val 成員的值填充它所指向的 int 值。如果 flag 成員爲 NULL ,在 getopt_long() 遇到此選項時,將返回 val 中的值;通過將 val 設置爲選項的 short 參數,可以在不添加任何其他代碼的情況下使用 getopt_long()—— 處理 while loop 和 switch 的現有 getopt() 將自動處理此選項。代碼示例可以參見 reference 5 。
Reference:
http://www.cs.caltech.edu/courses/cs11/material/c/mike/misc/cmdline_args.html
http://www.ibm.com/developerworks/cn/linux/shell/clutil/index.html
http://linux.chinaitlab.com/command/4198.html
http://www.gnu.org/software/libtool/manual/libc/Getopt.html
http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html