服務計算 - 3 Golang開發Linux命令列實用程式 - selpg
文章目錄
Golang開發Linux命令列實用程式 - selpg
1. 介紹
- Linux命令列使用程式 -
selpg
,這個名稱代表SELect PaGes
。selpg允許使用者指定從輸入文字抽取的頁的範圍,這些輸入文字可以來自檔案或另一個程序。 - 關於selpg的詳細介紹,參照
- 實現
slepg
程式碼
2. 設計與實現
2.1 設計思路
-
理解
selpg
命令的功能以及處理流程。 -
selpg
命令涉及的基本操作- 檔案的讀寫
- 從終端獲取輸入以及在終端輸出
2.2 功能模組劃分與實現
-
構建命令列引數的結構體,由於引數使用的比較多,可以使用一個結構體儲存起來,使用起來比較方便
type selpg_args struct { startPage int
-
引數解析
- 使用pflag包對命令列輸入引數進行解析
- 使用 pflag 替代 goflag 以滿足 Unix 命令列規範, 參考:Golang之使用Flag和Pflag
- 獲得
flag
引數後pflag.Parse()
函式才能把引數解析出來 - 使用
pflag.Args()
// 解析獲取引數 func getArgs(args *selpg_args) { pflag.IntVarP(&(args.startPage), "startPage", "s", -1, "start page") pflag.IntVarP(&(args.endPage), "endPage", "e", -1, "end page") pflag.IntVarP(&(args.pageLen), "pageLen", "l", 72, "the length of page") pflag.BoolVarP(&(args.pageType), "pageType", "f", false, "page type") pflag.StringVarP(&(args.outDestination), "outDestination", "d", "", "print destination") pflag.Parse() other := pflag.Args() // 其餘引數 if len(other) > 0 { args.inFile = other[0] } else { args.inFile = "" } }
-
引數檢查
-
檢查輸入引數的合法性
- 是否輸入了起始頁和結束頁
- 起始頁大於1小於結束頁以及不能溢位(
MaxInt32
) - 結束頁大於起始頁並且不能溢位(
MaxInt32
)
-
遇到不合法則輸出錯誤同時結束程式
// 檢查引數合法性 func checkArgs(args *selpg_args) { if args.startPage == -1 || args.endPage == -1 { os.Stderr.Write([]byte("You shouid input like selpg -sNumber -eNumber ... \n")) os.Exit(0) } if args.startPage < 1 || args.startPage > math.MaxInt32 { os.Stderr.Write([]byte("You should input valid start page\n")) os.Exit(0) } if args.endPage < 1 || args.endPage > math.MaxInt32 || args.endPage < args.startPage { os.Stderr.Write([]byte("You should input valid end page\n")) os.Exit(0) } if (!args.pageType) && (args.pageLen < 1 || args.pageLen > math.MaxInt32) { os.Stderr.Write([]byte("You should input valid page length\n")) os.Exit(0) } }
-
-
執行命令
-
執行命令部分主要涉及到檔案的讀取以及寫入或者標準輸入的獲取以及命令列下輸出,我們使用到bufio包,至於
-dXXX
實現,使用到 os/exec包 -
bufio包介紹與使用
-
bufio包
實現了帶快取的 I/O 操作,它封裝一個io.Reader
或io.Writer
物件,使其具有快取和一些文字讀寫功能-
func NewReaderSize(rd io.Reader, size int) *Reader
NewReaderSize
將rd
封裝成一個帶快取的bufio.Reader
物件,快取大小由size
指定(如果小於 16 則會被設定為 16)。如果rd
的基型別就是有足夠快取的bufio.Reader
型別,則直接將rd 轉換為基型別返回。 -
func NewReaderSize(rd io.Reader, size int) *Reader
NewReader
相當於NewReaderSize(rd, 4096)
-
func (reader *Reader) ReadBytes(delim byte) (line []byte, err error)
ReadBytes
在reader
中查詢delim
並讀出delim
及其之前的所有資料。如果ReadBytes
在找到delim
之前遇到錯誤,則返回遇到錯誤之前的所有資料,同時返回遇到的錯誤(通常是io.EOF
)。 只有當ReadBytes
找不到delim
時,err
才不為nil
-
-
-
os/exec包介紹與使用
-
os/exec
包執行外部命令。它包裝了os.StartProcess
函式以便更容易的修正輸入和輸出,使用管道連線I/O,以及作其它的一些調整。-
func Command(name string, arg ...string) *Cmd
command返回cmd結構來執行帶有相關引數的命令,它僅僅設定cmd結構中的Path和Args引數,如果name引數中不包含路徑分隔符,command使用LookPath來解決路徑問題,否則的話就直接使用name;Args直接跟在command命令之後,所以在Args中不許要新增命令。我們用該命令建立一個命令物件,引數為子程序路徑和子程序引數(可選)
// 指定執行的程式,實現模擬的印表機 cmd := exec.Command("./" + args.outDestination)
-
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe返回一個pipe,這個管道連線到command的標準錯誤,當command命令退出時,Wait將關閉這些pipe
-
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipe返回一個連線到command標準輸入的管道pipe。我們可以通過在此處寫入傳輸資訊,然後作為子程序的標準輸入。
-
-
對於
-dXXX
的實現,建立一個子程序,讓其模擬印表機,使用管道將資料傳輸給子程序,子程序讀取管道內傳輸的資訊並且打印出來。
-
-
處理流程
-
判斷是否指定輸入檔案,為空則將標準輸入作為輸入,否則為檔案流
-
判斷是否為
-d
型別 -
判斷輸入型別為
-l
還是-f
,依據要求讀取輸入,在輸出到標準輸出 -
判斷起始頁數以及結束頁數是否滿足實際標準
// 執行命令 func processInput(args *selpg_args) { // read the file var reader *bufio.Reader if args.inFile == "" { reader = bufio.NewReader(os.Stdin) } else { fileIn, err := os.Open(args.inFile) defer fileIn.Close() if err != nil { os.Stderr.Write([]byte("Open file error\n")) os.Exit(0) } reader = bufio.NewReader(fileIn) } // output the file if args.outDestination == "" { // 輸出到當前命令列 outputCurrent(reader, args) } else { // 輸出到目的地 outputToDest(reader, args) } }
// 輸出到當前命令列 func outputCurrent(reader *bufio.Reader, args *selpg_args); // 輸出到指定目的地 func outputToDest(reader *bufio.Reader, args *selpg_args);
-
-
具體程式碼參見Github專案
-