1. 程式人生 > >使用 Golang 開發簡單的 CLI 程式 selpg

使用 Golang 開發簡單的 CLI 程式 selpg

開發實踐

實現過程

文件中講解得非常仔細,我是完全按照文章的思路,參考原 C 程式的結構,然後用 golang 進行實現。golang 中有用於命令列引數解析、檔案讀寫和輸入輸出的包,可以很方便地實現 CLI 程式的一些複雜功能。

selpg 的 golang 程式碼 下面對程式碼的各部分進行解釋,並對程式進行測試,檢查是否滿足文件中 使用 selpg 章節的要求

程式碼註釋

以註釋“==== imports=====”開始的行 匯入所需的包。

io,實現了一系列非平臺相關的 IO 相關介面和實現,比如提供了對 os 中系統相關的 IO 功能的封裝。我們在進行流式讀寫(比如讀寫檔案)時,通常會用到該包。

os/exec,執行外部命令,它包裝了 os.StartProcess 函式以便更容易對映到 stdin 和 stdout,並且利用 pipe 連線 I/O。

bufio,在 io 的基礎上提供了快取功能。在具備了快取功能後, bufio 可以比較方便地提供 ReadLine 之類的操作。

os,提供了對作業系統功能的非平臺相關訪問介面。介面為Unix風格。提供的功能包括檔案操作、程序管理、訊號和使用者賬號等。

fmt,實現格式化的輸入輸出操作,其中的 fmt.Printf() 和 fmt.Println() 是開發者使用最為頻繁的函式。

flag,提供命令列引數的規則定義和傳入引數解析的功能。絕大部分的 CLI 程式都需要用到這個包。(程式碼中使用 pflag 替代 flag 以滿足 Unix 命令列規範, 參考:

Golang之使用Flag和Pflag

以註釋“==== types =====”開始的行 這裡只定義了一個型別,即:selpg_args 結構。指向該型別變數的指標被傳遞到 process_args() 函式,函式執行後,它包含從引數處理過程獲得的值。 type 用來給型別一個短名 sp_args。

以註釋“==== globals ======”開始的行 Progname 是儲存名稱(命令就是通過該名稱被呼叫)的全域性變數,作為在錯誤訊息中顯示之用。用這種方法,即使您將 selpg 命令重新命名為別的名稱,新的名稱也將在訊息中顯示;您不必修改該程式碼。

main() 函式 宣告一個 selpg_args 結構變數 sa,用指標 &sa 呼叫函式 process_args()。process_args() 返回後,已解析的引數值在 sa 結構中;將該變數傳遞至函式 process_input() ,該函式選擇所需的頁並將其寫至指定的目的地。

如果程式碼中任何一處出現了使處理不能繼續進行之類的錯誤,那麼我們會檢索系統錯誤訊息(如果有的話),然後將它與我們自己的訊息一起顯示。隨後我們用錯誤碼呼叫 exit() 函式;對於本實用程式,我們已經選擇了對每個不同錯誤條件返回不同數字。

process_args() 函式 使用 pflag 繫結 sa中的變數,就能夠很簡單地將命令列引數解析並保持在 sa 結構中。然後對命令列引數執行錯誤檢查。

以註釋“check the command-line arguments”開始的行 我們檢查是否傳遞了最小數目(三)的命令列引數。這三個引數是:

  • 命令名本身
  • -s Number 選項
  • -e Number 選項

如果少於三個引數,則列印訊息並退出。請注意對 usage() 函式的呼叫;這讓使用者知道呼叫實用程式的正確方法。這是編寫通用實用程式時應該遵守的又一個約定。

以註釋“handle 1st arg - start page”開始的行 檢查指示起始頁選項的引數名是否為“-s”。如果不是,則進行出錯退出。 如果沒有問題,用 INT_MAX 檢查引數值是否為這個平臺的有效正整數。

以註釋“handle 2nd arg - end page”開始的行 進行的檢查和操作與第一個引數基本相同。唯一的額外工作是檢查所給的結束頁不小於起始頁。

以註釋“now handle optional args”開始的行 處理餘下的引數 page_len 和 in_filename。(page_type 直接由 pflag 解析就可以了)

process_input() 函式 如果命令列上沒有給出檔名引數,我們就使用標準輸入和標準輸出。否則,我們開啟指定的檔案進行讀寫。如果檔案不能開啟,則進行出錯退出。然後根據 page_type 判斷是由換頁符定界還是由行數定界,進行相應的輸出。 要點:

  • golang 檔案讀寫、讀環境變數,使用 os 包
  • “-dXXX” 實現,請自己查 os/exec 庫,例如案例 Command,管理子程序的標準輸入和輸出通常使用 io.Pipe,具體案例見 Pipe

測試 selpg

測試檔案"test.txt"為數字1~30,每個數字佔一行

  • selpg,該命令輸入引數不符合要求,報錯

  • selpg -s 1 -e 1 test.txt,該命令將把“test.txt”的第 1 頁(預設10行為一頁)寫至標準輸出(也就是螢幕),因為這裡沒有重定向或管道。

  • selpg -s 1 -e 1 < test.txt,selpg 讀取標準輸入,而標準輸入已被 shell/核心重定向為來自“test.txt”而不是顯式命名的檔名引數。輸入的第 1 頁被寫至螢幕。

  • hello | selpg -s 1 -e 3 -f,hello程式的標準輸出(見下圖)被 shell/核心重定向至 selpg 的標準輸入。將第 1 頁到第 3 頁(由換頁符定界)寫至 selpg 的標準輸出(螢幕)。 在這裡插入圖片描述

  • selpg -s 1 -e 1 -l 5 test.txt >out.txt,selpg 將第 1 頁(5行為一頁)寫至標準輸出;標準輸出被 shell/核心重定向至“out.txt”。

  • selpg -s 3 -e 5 test.txt 2>error.txt,selpg 將第 3 頁到第 5 頁寫至標準輸出(螢幕);所有的錯誤訊息被 shell/核心重定向至“error.txt”。請注意:在“2”和“>”之間不能有空格;這是 shell 語法的一部分