天天看點

使用 getopt() 、getopt_long()、getopt_long_only()進行指令行處理

 簡介: 所有 UNIX® 程式甚至那些具有圖形使用者界面(graphical user interface,GUI)的程式,都能接受和處理指令行選項。對于某些程式,這是與其他程式或使用者進行互動的主要手段。具有可靠的複雜指令行參數處理機制,會使得您的應用程式更好、更有用。不過很多開發人員都将其寶貴的時間花在了編寫自己的指令行解析器,卻不使用 <code>getopt()</code>,而後者是一個專門設計來減輕指令行處理負擔的庫函數。請閱讀本文,以了解如何讓 <code>getopt()</code> 在全局結構中記錄指令參數,以便随後随時在整個程式中使用。

<a>引言</a>

在早期的 UNIX® 中,其指令行環境(當時的唯一使用者界面)包含着數十種小的文本處理工具。這些工具非常小,通常可很好地完成一項工作。這些工具通過較長的指令管道連結在一起,前面的程式将其輸出傳遞給下一個程式以作為輸入,整個過程由各種指令行選項和參數加以控制。

正是 UNIX 的這方面的特征使其成為了極為強大的處理基于本文的資料的環境,而這也是其在公司環境中的最初用途之一。在指令管道的一端輸入一些文本,然後在另一端檢索經過處理的輸出。

指令行選項和參數控制 UNIX 程式,告知它們如何動作。作為開發人員,您要負責從傳遞給您程式的 <code>main()</code> 函數的指令行發現使用者的意圖。本文将示範如何使用标準 <code>getopt()</code> 和 <code>getopt_long()</code> 函數來簡化指令行處理工作,并讨論了一項用于跟蹤指令行選項的技術。

<a>開始之前</a>

<a>指令行</a>

<a>清單 1. 聲明 <code>main()</code> 函數的兩種方式</a>

第一種方式使用的是指向 <code>char</code> 指針數組,現在似乎很流行這種方式,比第二種方式(其指針指向多個指向 <code>char</code> 的指針)略微清楚一些。由于某些原因,我使用第二種方式的時間更多一些,這可能源于我在高中時艱難學習 C 指針的經曆。對于所有的用途和目的,這兩種方法都是一樣的,是以可以使用其中您自己最喜歡的方式。

當 C 運作時庫的程式啟動代碼調用您的 <code>main()</code> 時,已經對指令行進行了處理。<code>argc</code> 參數包含參數的計數值,而 <code>argv</code> 包含指向這些參數的指針數組。對于 C 運作時庫,arguments 是程式的名稱,程式名後的任何内容都應該使用空格加以分隔。

<a>清單 2. argv 的内容</a>

示例代碼将示範一個假想的 doc2html 程式的指令行處理。該 doc2html 程式将某種類型的文檔轉換為 HTML,具體由使用者指定的指令行選項控制。它支援以下選項:

<code>-I</code>——不建立關鍵字索引。

<code>-l lang</code>——轉換為使用語言代碼 <code>lang</code> 指定的語言。

<code>-o outfile.html</code>——将經過轉換的文檔寫入到 outfile.html,而不是列印到标準輸出。

<code>-v</code>——進行轉換時提供詳細資訊;可以多次指定,以提高診斷級别。

将使用其他檔案名稱來作為輸入文檔。

您還将支援 <code>-h</code> 和 <code>-?</code>,以列印幫助消息來提示各個選項的用途。

<a>簡單指令行處理: <code>getopt()</code></a>

<a>清單 3. <code>getopt()</code> 原型</a>

給定了指令參數的數量 (<code>argc</code>)、指向這些參數的數組 (<code>argv</code>) 和選項字元串 (<code>optstring</code>) 後,<code>getopt()</code> 将傳回第一個選項,并設定一些全局變量。使用相同的參數再次調用該函數時,它将傳回下一個選項,并設定相應的全局變量。如果不再有識别到的選項,将傳回<code>-1</code>,此任務就完成了。

<code>getopt()</code> 所設定的全局變量包括:

<code>optarg</code>——指向目前選項參數(如果有)的指針。

<code>optind</code>——再次調用 <code>getopt()</code> 時的下一個 argv 指針的索引。

<code>optopt</code>——最後一個已知選項。

對于每個選項,選項字元串 (<code>optstring</code>) 中都包含一個對應的字元。具有參數的選項(如示例中的 <code>-l</code> 和 <code>-o</code> 選項)後面跟有一個 <code>:</code> 字元。示例所使用的 <code>optstring</code> 為 <code>Il:o:vh?</code>(前面提到,還要支援最後兩個用于列印程式的使用方法消息的選項)。

可以重複調用 <code>getopt()</code>,直到其傳回 <code>-1</code> 為止;任何剩下的指令行參數通常視為檔案名或程式相應的其他内容。

<a><code>getopt()</code> 的使用</a>

<a>清單 4. 系統頭檔案 </a>

每個指令行選擇都有一個對應的選項,而其他變量用于存儲輸出檔案名、指向輸入檔案清單的指針和輸入檔案數量。

<a>清單 5. 全局參數存儲和選項字元串</a>

選項字元串 <code>optString</code> 告知 <code>getopt()</code> 可以處理哪個選項以及哪個選項需要參數。如果在處期間遇到了其他選項,<code>getopt()</code> 将顯示一個錯誤消息,程式将在顯示了使用方法消息後退出。

<a>清單 6. 存根</a>

<a>清單 7. 初始化</a>

<a>清單 8. 使用 <code>getopt()</code> 處理 argc/argv</a>

<a>清單 9. 開始工作</a>

好,工作完成,非常漂亮。現在就可以不再往下讀了。不過,如果您希望程式符合 90 年代末期的标準并支援 GNU 應用程式中流行的長 選項,則請繼續關注下面的内容。

<a>複雜指令行處理: <code>getopt_long()</code></a>

在 20 世紀 90 年代(如果沒有記錯的話),UNIX 應用程式開始支援長選項,即一對短橫線(而不是普通短 選項所使用的單個短橫線)、一個描述性選項名稱還可以包含一個使用等号連接配接到選項的參數。

幸運的是,可以通過使用 <code>getopt_long()</code> 向程式添加長選項支援。您可能已經猜到了,<code>getopt_long()</code> 是同時支援長選項和短選項的<code>getopt()</code> 版本。

<a>清單 10. <code>getopt_long()</code> 的選項</a>

<code>name</code> 成員是指向長選項名稱(帶兩個短橫線)的指針。<code>has_arg</code> 成員設定為 <code>no_argument</code>、<code>optional_argument</code>, 或<code>required_argument</code>(均在 <code>getopt.h</code> 中定義)之一,以訓示選項是否具有參數。如果 flag 成員未設定為 NULL,在處理期間遇到此選項時,會使用 <code>val</code> 成員的值填充它所指向的 <code>int</code> 值。如果 flag 成員為 <code>NULL</code>,在 <code>getopt_long()</code> 遇到此選項時,将傳回 <code>val</code> 中的值;通過将 <code>val</code> 設定為選項的 <code>short</code> 參數,可以在不添加任何其他代碼的情況下使用 <code>getopt_long()</code>——處理 <code>while loop</code> 和 <code>switch</code> 的現有 <code>getopt()</code> 将自動處理此選項。

這已經變得更為靈活了,因為各個選項現在可以具有可選參數了。更重要的是,僅需要進行很少的工作,就可以友善地放入現有代碼中。

<a>使用 <code>getopt_long()</code></a>

由于 getopt_long_demo 幾乎與剛剛讨論的 getopt_demo 代碼一樣,是以我将僅對更改的代碼進行說明。由于現在已經有了更大的靈活性,是以還将添加對 <code>--randomize</code> 選項(沒有對應的短選項)的支援。

<a>清單 11. 其他頭檔案</a>

<a>清單 12. 擴充後的參數</a>

<a>清單 13. 新的經改進的選項處理</a>

我還添加了 <code>0</code> 的 case,以便處理任何不與現有短選項比對的長選項。在此例中,隻有一個長選項,但代碼仍然使用 <code>strcmp()</code> 來確定它是預期的那個選項。

這樣就全部搞定了;程式現在支援更為詳細(對臨時使用者更加友好)的長選項。

<a>總結</a>

UNIX 使用者始終依賴于指令行參數來修改程式的行為,特别是那些設計作為小工具集合 (UNIX 外殼環境)的一部分使用的實用工具更是如此。程式需要能夠快速處理各個選項和參數,且要求不會浪費開發人員的太多時間。畢竟,幾乎沒有程式設計為僅處理指令行參數,開發人員更應該将精力放在程式所實際進行的工作上。

<code>getopt()</code> 函數是一個标準庫調用,可允許您使用直接的 while/switch 語句友善地逐個處理指令行參數和檢測選項(帶或不帶附加的參數)。與其類似的 <code>getopt_long()</code> 允許在幾乎不進行額外工作的情況下處理更具描述性的長選項,這非常受開發人員的歡迎。

既然已經知道了如何友善地處理指令行選項,現在就可以集中精力改進您的程式的指令行,可以添加長選項支援,或添加之前由于不想向程式添加額外的指令行選項處理而擱置的任何其他選項。

不要忘記在某處記錄您所有的選項和參數,并提供某種類型的内置幫助函數來為健忘的使用者提供幫助。

<a href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#ibm-pcon">回頁首</a>

<a>下載下傳</a>

描述

名字

大小

下載下傳方法

Sample getopt() program

au-getopt_demo.zip

23KB

<a href="http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=249919&amp;filename=au-getopt_demo.zip&amp;method=http&amp;locale=zh_CN">HTTP</a>

Sample getopt_long() program

au-getopt_long_demo.zip

24KB

<a href="http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=249919&amp;filename=au-getopt_long_demo.zip&amp;method=http&amp;locale=zh_CN">HTTP</a>

<a href="http://www.ibm.com/developerworks/cn/whichmethod.html">關于下載下傳方法的資訊</a>

     本文轉自 驿落黃昏 51CTO部落格,原文連結:http://blog.51cto.com/yiluohuanghun/1182459,如需轉載請自行聯系原作者

繼續閱讀