1. 程式人生 > >用Go寫一個簡單的Selpg命令列程式

用Go寫一個簡單的Selpg命令列程式

設計說明

【程式簡介】 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】用於記錄錯誤資訊,可將錯誤資訊輸出到該文件

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述