用Go寫一個簡單的Selpg命令列程式
阿新 • • 發佈:2018-12-13
設計說明
【程式簡介】 Selpg從標準輸入或從作為命令列引數給出的檔名讀取文字輸入。它允許使用者指定來自該輸入並隨後將被輸出的頁面範圍,然後輸出到標準輸出或是檔案中。
【程式設計】 程式的功能分為以下幾個部分:
- 讀取一條命令列輸入的指令
- 解析命令,分析其中的引數
- 實現命令請求的操作
- 輸入命令有誤時進行提示
一、【讀取輸入指令並解析】
輸入指令的格式為
selpg [--s start_page] [--e end_page] [--l lines_per_page | --f ] [ --d dest ] [ input_source ]
其中各個引數的含義如下:
- –s 從輸入的第幾頁開始讀起,預設值為-1
- –e 讀到輸入的第幾頁,預設值為-1
- –l 當換頁模式為【l】時,每一頁的行數,預設值為72
- –f 將換頁模式設定為【f】,即遇到分頁符時自動換頁
- (注:–l與–f是互斥的兩個屬性)
- –d 輸出的地址
- 【input_source】沒有flag值,指輸入檔案的檔名,預設為空,即標準輸入
使用一個結構體儲存指令,成員包括以上的引數
type selpg_Args struct { start_page int end_page int page_len int page_typestring // 'l' for lines-delimited, 'f' for form-feed-delimited default is 'l' print_dest string input_source string // 輸入途徑,預設為鍵盤輸入 }
使用【flag.XXXVar】依次繫結指令中的各個標識值與對應的變數
flag.IntVar(&sa.start_page, "s", -1, "the start Page") flag.IntVar(&sa.end_page, "e", -1, "the end Page") flag.IntVar(&sa.page_len, "l", 72, "the length of the page") flag.StringVar(&sa.print_dest, "d", "", "the destiny of printing") //預設值預設 /*檢查命令中是否含有-f 如果有,則selpg在輸入中尋找換頁符,並將其作為頁定界符 若沒有,則按照輸入的-l的長度作為頁的長度 */ exist_f := flag.Bool("f", false, "")*
二、【實現命令請求的操作】
在讀取完引數後,通過引數的值來實現對應的操作。
先初始化變數,通過給定的引數決定是否呼叫os庫中的標準輸入和輸出
fin := os.Stdin
fout := os.Stdout
cur_page := 1 //當前頁
cur_line := 0 //當前行
var inpipe io.WriteCloser
var err error
判斷輸入方式
//判斷是鍵盤讀入or檔案讀入
if sa.input_source != "" { //非預設->檔案
fin, err = os.Open(sa.input_source)
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: input file \"%s\" does not exist!\n", sa.input_source)
//fmt.Println(err)
usage()
os.Exit(1)
}
defer fin.Close()
}
判斷輸出方式
if sa.print_dest != "" {
cmd := exec.Command("grep", "-nf", "keyword")
inpipe, err = cmd.StdinPipe()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer inpipe.Close() //最後執行
cmd.Stdout = fout
cmd.Start()
}
判斷分頁方式
//判斷分頁方式是-l還是-f
//按照-l來分頁
if sa.page_type == "l" {
line := bufio.NewScanner(fin)
//按行來讀
for line.Scan() {
//還沒輸出完
if cur_page >= sa.start_page && cur_page <= sa.end_page {
//輸出到cmd視窗
fout.Write([]byte(line.Text() + "\n"))
if sa.print_dest != "" {
//輸出到檔案
inpipe.Write([]byte(line.Text() + "\n"))
}
}
cur_line++
if cur_line == sa.page_len {
cur_page++
cur_line = 0
}
}
} else { //按照-f來分頁
rd := bufio.NewReader(fin)
for {
//按頁讀
page, ferr := rd.ReadString('\f')
if ferr != nil || ferr == io.EOF {
if ferr == io.EOF {
if cur_page >= sa.start_page && cur_page <= sa.end_page {
fmt.Fprintf(fout, "%s", page)
}
}
break
}
page = strings.Replace(page, "\f", "", -1)
cur_page++
if cur_page >= sa.start_page && cur_page <= sa.end_page {
fmt.Fprintf(fout, "%s", page)
}
}
}
三、【錯誤處理】
對於非標準格式的輸入進行了提示
(使用pflag代替goflag,則輸入引數時由【-s】變為【–s】)
還有開啟檔案失敗、實際輸出頁數小於-l的引數等錯誤都會有對應的提示
【測試結果】 一共建立了4個文件,分別是【input1】、【input2】、【output1】、【error_file】
- 【input1】是一個20行的輸入文件,每行的內容為【line+數字】
- 【input2】內容與【input1】相仿,但每隔4行的結尾插入一個換頁符【/f】
- 【output1】是一個空文件,用作輸出文件
- 【error_file】用於記錄錯誤資訊,可將錯誤資訊輸出到該文件