當(dāng)我們?yōu)長inux編寫程序時,我們必須考慮到程序會運行在多任務(wù)環(huán)境下,。這就意味著多個程序會同時運行,,并且共享機器資源,例如內(nèi)存,,磁盤空間以及CPU周期,。也許在同一時刻會一個程序多個實例在運行。這時最為重要的就是這些程序之間不會相互影響,,彼此清楚其周邊環(huán)境,,同時也要正確的運行以避免沖突,例如與另一個程序同時試著寫入相同的文件等,。 在這一章,,我們將會討論程序執(zhí)行的環(huán)境,他們?nèi)绾问褂铆h(huán)境來得到有關(guān)操作環(huán)境的信息,,以及這些程序用戶如何改變其行為,。具體的說,我們會討論下面內(nèi)容: 向程序傳遞參數(shù) 環(huán)境變量 查看時間信息 臨時文件 得到用戶以及主機的信息 記錄以及配置日志消息 發(fā)現(xiàn)系統(tǒng)的限制 程序參數(shù) 當(dāng)一個使用C語言編寫的Linux或是Unix程序運行時,,他由main函數(shù)開始執(zhí)行,。對于這些程序來說,main函數(shù)聲明為 int main(int argc, char *argv[]) 在這里argc是程序參數(shù)的個數(shù),,而argv是一個代表參數(shù)本身的字符串?dāng)?shù)組,。 有時我們也會看到Linux的C程序簡單的聲明為 main() 這仍然可以正常工作,因為返回類型默認(rèn)為int型,,而在函數(shù)不需要的常規(guī)參數(shù)并不需要聲明,。argc與argv仍然存在,但是如果我們不進(jìn)行聲明,,我們就沒有辦法使用,。 當(dāng)操作系統(tǒng)啟動一個新程序時,argc與argv就會被設(shè)置,,并傳遞給main,。這些參數(shù)通常是由另一個程序來提供的,通常是請求操作系統(tǒng)執(zhí)行一個新程序的shell,。shell會讀取所給定的命令行參數(shù),,將其分為單個的單詞,并且用他們來設(shè)置argv數(shù)組,。記住,,在argc與argv被設(shè)置之前,Linux shell通常會對文件名參數(shù)執(zhí)行通配符擴展,而MS-DOS shell會期望程序接受帶有通配符的參數(shù),,并且執(zhí)行他們自己的通配符擴展,。 例如,如果我們?yōu)閟hell提供下面的命令: $ myprog left right ‘a(chǎn)nd center’ myprog程序就會用下列參數(shù)來啟動main函數(shù): argc: 4 argv: {“myprog”, “l(fā)eft”, “right”, “and center”} 注意,,參數(shù)個數(shù)包括程序本身的名字,,并且argv數(shù)組包含程序名字作為其第一個元素,argv[0],。因為我們在shell命令中使用了引號,,第四個參數(shù)包含一個帶有空格的字符串。 如果我們使用過ISO/ANSI C編寫過程序,,我們就會對這些內(nèi)容感到熟悉,。main參數(shù)相對應(yīng)于shell腳本中的位置參數(shù),$0,$1,,依次類推,。然而ISO/ANSI C說明main必須返回int,X/Open的描述包含上面給出的顯示聲明,。 命令行參數(shù)對于向程序傳遞信息是十分有用的,。例如,我們可以在一個數(shù)據(jù)庫程序中使用命令行參數(shù)來傳遞我們希望使用的數(shù)據(jù)庫名,,這樣就可以允許我們在多個數(shù)據(jù)庫上使用相同的程序,。許多實用程序也使用命令行參數(shù)來改變他們的行為或是設(shè)置選項。我們可以使用帶有短劃線的命令行參數(shù)來設(shè)置這些所謂的標(biāo)記,,或是開關(guān),。例如,sort程序帶有一個開關(guān)來反轉(zhuǎn)通常的排列順序,。 $ sort -r file 命令行參數(shù)十分常見,,而且恰當(dāng)?shù)氖褂脤τ谑褂梦覀兂绦虻娜藖碚f確實是一個極大的幫助。過去,,所有的實用程序都實現(xiàn)了他們各自的命令選項方法,,這就造成混亂。例如,,看一下下面這些命令讀取參數(shù)的方式: $ tar cvfB /tmp/file.tar 1024 $ dd if=/dev/fd0 of=/tmp/file.dd bs=18k $ ls -lstr $ ls -l -s -t -r 所有的命令行參數(shù)都應(yīng)以一個短劃線開始,,并且由一個字母或是數(shù)字組成。沒有更多參數(shù)的選項可以在短劃線后組織在一起,。所以上面顯示的兩個ls命令都遵守了這個約定,。如果選項需要一個單獨的參數(shù),那么可以在其后跟一個值,。dd命令的例子并沒有遵守這個規(guī)則,,他使用了多個字符選項,并且沒有以短劃線開始(if=/dev/fdo);而tar命令將其選項與值進(jìn)行完全的分離,。 另外一些程序的缺點就是使用選項+x來執(zhí)行與-x相反的功能,。 正如我們所看到的,如果不使用特殊的格式處理,,記住所有這些程序選項的順序和意義是相當(dāng)困難的,。通常,唯一的資源就是使用一個-h(help)選項,,或者是man頁,。正如我們在后面將會看到,getopt為這些問題提供了一個靈巧的解決辦法,。但是現(xiàn)在,,讓我們看一下所傳遞的程序參數(shù)的處理。 試驗--程序參數(shù) 這里是一個程序,,args.c,,來檢測他自己的參數(shù): #include <stdio.h> int main(int argc, char *argv[]) { int arg; for(arg = 0; arg < argc; arg++) { if(argv[arg][0] == ‘-’) printf(“option: %s/n”, argv[arg]+1); else printf(“argument %d: %s/n”, arg, argv[arg]); } exit(0); } 當(dāng)我們運行這個程序時,他只是打印出其參數(shù)與檢測選項,。其目的在程序讀取一個字符串參數(shù)以及一個由-f選項引入的可選的文件名參數(shù),。同時也定義了其他的選項。 $ ./args -i -lr ‘hi there’ -f fred.c argument 0: args option: i option: lr argument 3: hi there option: f argument 5: fred.c 工作原理 程序只是簡單的使用程序計數(shù)argc設(shè)置一個循環(huán)來檢測所有的程序參數(shù),。他通過查找一個初始短劃線來檢測選項,。 在這個例子中,如果我們的設(shè)計只是-l與-r是可用的,,那么也許我們就會失去將-lr本應(yīng)看作與-l -r相同的事實,。 X/Open描述為命令行選項定義了一個標(biāo)準(zhǔn)用法,同時在C程序中為提供命令行開關(guān)提供定義了一個標(biāo)準(zhǔn)程序接口:getopt函數(shù),。 getopt 為了幫助我們遵守這些規(guī)則,,Linux為我們提供了getopt函數(shù),他支持帶值與不帶值的選項用法,,并且使用簡單,。 #include <unistd.h> int getopt(int argc, char *const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt; getopt函數(shù)讀取傳遞給程序的main函數(shù)的argc與argv參數(shù)以及一個選項描述符字符串,這個字符串通知getopt為這個程序定義了哪些選項,,以及這些選項是否有相關(guān)聯(lián)的值,。optstring只是一個簡單的字符列表,每一個代表一個字符選項,。如果一個字符后跟有一個冒號,,那么他表明這個選項有一個與其相關(guān)聯(lián)的值,應(yīng)讀取作為下一個參數(shù),。bash中的getopts命令有著相類似的功能,。 例如,,下面的調(diào)用可以用來處理我們前面的例子: getopt(argc, argv, “if:lr”); 他允許簡單的選項-i,-l,-r,,以及后跟文件名參數(shù)的-f選項,。使用相同的參數(shù)但是以不同的順序調(diào)用這個命令會改變其行為。當(dāng)我們遇到后面的示例代碼時可以進(jìn)行嘗試,。 getopt的返回結(jié)果是argv數(shù)組中下一個選項字符(如果有),。我們重復(fù)調(diào)用getopt來依次得到每個選項。他有下面的行為: 如果選項帶有一個參數(shù)值,,那么這個值是由外部變量optarg來指向的,。 當(dāng)沒有選項要進(jìn)行處理時,getopt會返回-1,。一個特殊的參數(shù),,--,,會使得getopt停止搜索,。 如果有一個不可識別的選項,他會返回?,,并且選項會存儲在外部變量optopt中,。 如果一個選項需要一個參數(shù)值(例如我們例子中的-f),但是卻沒有參數(shù)值時,,getopt會返回,。 外部變量optind設(shè)置為要處理的下一個參數(shù)的索引。getopt會使用這個變量來記錄他處理到了哪里,。程序通常并不需要這個變量,。當(dāng)所有的選項參數(shù)處理完畢時,optind會指示在argv數(shù)組末尾的何處可以在找到其余的參數(shù),。 一些版本的getopt會在遇到第一個非選項參數(shù)時停止,,返回-1并且設(shè)置optind。其他的一些版本,,例如Linux所提供的,,可以處理程序參數(shù)中的所有選項。注意,,在這個例子中,,getopt會重改定argv數(shù)據(jù),這樣所有的非選項參數(shù)就可以組織在一起,,由argv[optind]處開始,。對于GNU版本的getopt,其行為是由環(huán)境變量POSIXLY_CORRECT來控制的,。如果設(shè)置,,getopt就會在第一個非選項參數(shù)處停止,。另外,一些getopt實現(xiàn)會為不可知的選項打印錯誤信息,。注意,,POSIX的描述說明是,如果opterr變量為非零,,getopt就會向stderr打印一個錯誤消息,。 試驗--getopt 現(xiàn)在我們在我們的例子中使用getopt,并且將這個新程序稱之為argopt.c: #include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { int opt; while((opt = getopt(argc, argv, “if:lr”)) != -1) { switch(opt) { case ‘i’: case ‘l’: case ‘r’: printf(“option: %c/n”, opt); break; case ‘f’: printf(“filename: %s/n”, optarg); break; case ‘:’: printf(“option needs a value/n”); break; case ‘?’: printf(“unknown option: %c/n”, optopt); break; } } for(; optind < argc; optind++) printf(“argument: %s/n”, argv[optind]); exit(0); } 現(xiàn)在,,當(dāng)我們運行這個程序時,,我們發(fā)現(xiàn)所有的命令行參數(shù)被自動處理了: $ ./argopt -i -lr ‘hi there’ -f fred.c -q option: i option: l option: r filename: fred.c argopt: invalid option-—q unknown option: q argument: hi there 工作原理 程序會重復(fù)調(diào)用getopt來處理選項參數(shù),直到?jīng)]有選項需要處理為止,,此時getopt會返回-1,。這個動作會在每一個選項上執(zhí)行,包括處理不可知的選項以有沒有選項參數(shù)值的情況,。依據(jù)我們的getopt版本,,我們得到的輸出與上面的與許不同,尤其是錯誤信息,,但是意義是清楚的,。 一旦所有的選項都被處理了,程序就會簡單的打印出剩余的參數(shù),,這與前面的例子相同,,但是卻由optind開始的。 getopt_long 許多Linux程序同時會接受比我們在上面的例子中所使用的單字符選項更有意義的參數(shù),。GNU C庫包含一個被稱之為getopt_long的getopt版本,,他可以接受由雙短劃線引入的所謂長參數(shù)。 我們使用getopt_long來創(chuàng)建我們上面例子程序的一個新版本,,這樣他就可以使用與我們的選項等同的長參數(shù)來調(diào)用了,。 事實上,新的長選項與原始的單字符選項可以混合,。只要他們存在不同,,長選項也可以簡寫。帶有參數(shù)的長選項可以--option=value的格式給出,,如下所示: $ ./longopt —init –l —file=fred.c ‘hi there’ option: i option: l filename: fred.c argument: hi there 新程序longopt.c如下所示: #include <stdio.h> #include <unistd.h> #define _GNU_SOURCE #include <getopt.h> int main(int argc, char *argv[]) { int opt; struct option longopts[] = { {“initialize”, 0, NULL, ‘i’}, {“file”, 1, NULL, ‘f’}, {“l(fā)ist”, 0, NULL, ‘l’}, {“restart”, 0, NULL, ‘r’}, {0,0,0,0}}; while((opt = getopt_long(argc, argv, “if:lr”, longopts, NULL)) != -1) { switch(opt) { case ‘i’: case ‘l’: case ‘r’: printf(“option: %c/n”, opt); break; case ‘f’: printf(“filename: %s/n”, optarg); break; case ‘:’: printf(“option needs a value/n”); break; case ‘?’: printf(“unknown option: %c/n”, optopt); break; } } for(; optind < argc; optind++) printf(“argument: %s/n”, argv[optind]); exit(0); } 工作原理 與getopt相比,,getopt_long帶有兩個額外的參數(shù)。第一個為一個結(jié)構(gòu)數(shù)組,,描述了長選項并且通知getopt_long如何來處理,。第二個額外參數(shù)是一個指向可以用作長選項版本的optind變量的指針;對于每一個識別的長選項,,他在長選項數(shù)組中的索引可以寫入這個變量,。在我們的例子中,,我們并不需要這個信息,所以我們使用NULL作為第二個額外參數(shù),。 長選項數(shù)組是由一些type struct option的結(jié)構(gòu)組成的,,每一個描述了長選項的行為。這個數(shù)組必須以一個包含零的結(jié)構(gòu)結(jié)束,。 長選項結(jié)構(gòu)定義在getopt.h中,,而且必須使用_GNU_SOURCE常包含,用來允許getopt_long功能: struct option { const char *name; int has_arg; int *flag; int val; }; 結(jié)構(gòu)的成員如下: name 長選項的名字,。只要他們的簡寫不與其他的選項沖突就可以被接受,。 has_arg 這個選項是否帶有參數(shù)。如果不需要參數(shù),,將其設(shè)置為0,;如果必須有一個參數(shù)值,將其設(shè)置為1,;如果有一個可選參數(shù),,將其設(shè)置為2。 flag 將其設(shè)置為NULL,,使得getopt_long在查找到這個選項時返回val中定的值,。否則,getopt_long會返回0,,并且將val的值寫入由flag所指向的變量。 val getopt_long為這個選項返回的值,。 要了解其他與getopt的GNU擴展相關(guān)聯(lián)的選項以及相關(guān)的功能,,可以查看getopt的手冊頁。 |
|