linux 命令列解析
從windows開發轉向linux開發的程式設計師,實際寫程式碼過程中還是有很多不同的,第一個遇到的不同可能是linux的命令列。windows 應用開發很少有命令列,而linux命令幾乎全有命令列,這是因為linux沒有操作介面,只能以命令列的方式來切換程式執行不同的命令,或調節命令的引數。比方說你要統計一個檔案有多少個字元,在windows下你可以直接用介面操作來選擇一個檔案,而在linux下你只能通過檔案引數來運作。
linux程序的命令列引數一般有,操作源,目標,對操作過程的控制或補充說明。有的命令操作源和目標都有,例如檔案複製cp命令。有的只有操作源或目標,例如進入指定目錄cd命令。有的操作源,目標都沒有,例如顯示時間命令date。有些命令列引數也可以看作是子命令,例如yum instatll redis,yum是程序名,install可以看作是他的子命令(或者你可以理解為yum-install),redis是操作源。大部分的linux指令都有操作源或目標。
雖然linux命令列引數的操作源或目標比較單一,但是Linux程序的命令列引數遠遠沒有那麼簡單,原因是對命令操作過程的控制或補充說明可能會比較複雜。有的控制命令有好幾個甚至幾十個,當然他們不可能同時都被使用。對於命令列的操作源或目標,一般沒有字首。例如,cp file1 file(其實也可以理解為cp --src file1 --dst file2 )。控制引數一般會有字首-,而且不同的指令引數的風格還可以不同。例如cp --help,--help只是一個引數,而tar -xzvf,xzvf是四個不同的引數。我們要討論的是如何正確的解析各種不同的命令列引數。
以常見的git程式來舉例,git功能非常強大,他有很多操作,例如add, commit, push, checkout等等,可以把他們看作是git的命令集。
例如,有如下命令:
git commit --amend -m "firt commit"
在上面的例子中,git是程序的名字,commit是git的一個子命令,--amend, -m是命令的引數,"first commit"是引數的值。
在我們的程式中,對於這個命令,我們要正確的解析出子命令commit,提交的描述為"first commit",以及amend ,即提交的補充說明為修改上次的描述。如果我們自己在程式中以解析字串的方式來解析這些命令列引數,估計要瘋掉了。前面說到,命令列引數的樣式不一樣,沒有字首的,有字首的,有字首的風格還不一樣。還好Linux 系統給我們提供了一些解析命令列引數的api 解放了苦逼的程式設計師們。
這些api是getopt,以及增強版的getopt_long。考慮到每個程序用到的命令列引數都可能不一樣,getopt把設定命令列引數的權力給了開發者自己,而不是用內定的字元來表示某種含義的引數。前面說了,有的命令列有引數,有的沒有,有的既可以有又可以沒有。所以要用一種簡單的方式,來表示這種含義。所以getopt的原型為:
int getopt(int argc, char *const argv[], const char *optstring);
前面兩個引數表示命令列引數的個數(以空格分割)和真實的命令列字串。optstring可以指定你要設定的引數字元以及引數的風格。例如"a:bc::de"表示這個程序可以有-a的命令列,而且後面必須有引數。例如-a 123或-a123 表示a這個命令列,123是引數。即使是-a -b,-b也要解析為-a的引數。:表示後面必須跟值。b沒有冒號表示,只有命令列,沒有引數。如果b後面有引數,例如-b123,則解析錯誤。如果是-b 123,則123與本次的-b無關。有兩個::的含義是,例如b::表示命令列可以有引數也可以沒有引數,有引數時引數必須緊跟命令列。例如-b123是正確的,表示有一個123引數。而-b 123表示沒有引數,123與-b沒有關係。
只有沒有引數的命令列才能合併,否則會被帶引數的命令列解析為引數。例如tar -zxvf,zxvf都為tar的命令列,且沒有引數。而帶兩個--的解析為一個字串命令列,例如上面的--amend ,amend是一個命令列。解析帶兩個--的命令列引數需要用到更高階的api,getopt_long。請參考:https://www.cnblogs.com/chasechoi/p/7644199.htmlhttps://www.cnblogs.com/chenliyang/p/6633739.html
getopt是解析帶有-命令列及其引數的。那沒有-或--的命令列,例如前面的git commit又該怎麼解析呢?其實getopt也幫我們考慮到了,他會修改全域性變數optind的值,表示再次呼叫 getopt() 時的下一個 argv指標的索引。有了這個索引我們可以很方便再次的遍歷argv,從而得到commit這個命令列。