服務計算——CLI 命令行實用程序開發

作業描述:CLI 命令行實用程序開發
作業中提供了兩種語言,一種語言是c的,一種語言是python。這兩種語言我都略懂一點,但卻深感自己學的不深,這裏嘗試看C語言版。
參考博客:
劉一 思否博客
CSDN有關博客

前期準備

CLI

CLI(Command Line Interface)實用程序是Linux下應用開發的基礎。正確的編寫命令行程序讓應用與操作系統融爲一體,通過shell或script使得應用獲得最大的靈活性與開發效率。Linux提供了cat、ls、copy等命令與操作系統交互;go語言提供一組實用程序完成從編碼、編譯、庫管理、產品發佈全過程支持;容器服務如docker、k8s提供了大量實用程序支撐雲服務的開發、部署、監控、訪問等管理任務;git、npm等都是大家比較熟悉的工具。儘管操作系統與應用系統服務可視化、圖形化,但在開發領域,CLI在編程、調試、運維、管理中提供了圖形化程序不可替代的靈活性與效率。

閱讀:開發 Linux 命令行實用程序

系統:centos7

開發實踐

使用 golang 開發 開發 Linux 命令行實用程序 中的 selpg

提示:

1、請按文檔 使用 selpg 章節要求測試你的程序
2、請使用 pflag 替代 goflag 以滿足 Unix 命令行規範, 參考:Golang之使用Flag和Pflag
3、golang 文件讀寫、讀環境變量,請自己查 os 包
4、“-dXXX” 實現,請自己查 os/exec 庫,例如案例 Command,管理子進程的標準輸入和輸出通常使用 io.Pipe,具體案例見 Pipe

具體實現:
1、根據文檔,此程序需要滿足以下幾個要求:
命令輸入
參數處理
參數規範檢測
讀取文件
處理文件
輸出文件

其中過程中若存在一些情況可以拋出錯誤。

代碼實現:

1、最開始需要做的是設置參數結構體:


type selpg_args struct {
    startPage  int
    endPage    int
    inFileName string
    pageLen    int
    pageType   bool
    printDest  string
}

輸入參數使用包中提供的pflag進行處理。

fun getselpg(args *selpg_args){
    pflag.IntVarP(&(args.startPage), "startPage", "s", -1, "Define startPage")
    pflag.IntVarP(&(args.endPage), "endPage", "e", -1, "Define endPage")
    pflag.IntVarP(&(args.pageLen), "pageLength", "l", 72, "Define pageLength")
    pflag.StringVarP(&(args.printDest), "printDest", "d", "", "Define printDest")
    pflag.BoolVarP(&(args.pageType), "pageType", "f", false, "Define pageType")
    pflag.Parse()
	argLeft := pflag.args()
	if len(argLeft)>0{
		args.inFileName = string(argLeft[0])
	} else {
		args.inFileName = ""
	}
}

2、命令行參數獲取之後,首先要進行參數檢查以儘量避免參數謬誤。出現錯誤時輸出問題並正常結束程序。參數正確則將各個參數值輸出到屏幕上。

func examselpg(args *selpg_args) {

	if (args.startPage == -1) || (args.endPage == -1) {
		fmt.Fprintf(os.Stderr, "\n[Error]The startPage and endPage can't be empty! Please check your command!\n")
		os.Exit(2)
	} else if (args.startPage <= 0) || (args.endPage <= 0) {
		fmt.Fprintf(os.Stderr, "\n[Error]The startPage and endPage can't be negative! Please check your command!\n")
		os.Exit(3)
	} else if args.startPage > args.endPage {
		fmt.Fprintf(os.Stderr, "\n[Error]The startPage can't be bigger than the endPage! Please check your command!\n")
		os.Exit(4)
	} else if (args.pageType == true) && (args.pageLen != 72) {
		fmt.Fprintf(os.Stderr, "\n[Error]The command -l and -f are exclusive, you can't use them together!\n")
		os.Exit(5)
	} else if args.pageLen <= 0 {
		fmt.Fprintf(os.Stderr, "\n[Error]The pageLen can't be less than 1 ! Please check your command!\n")
		os.Exit(6)
	} else {
		pageType := "page length."
		if args.pageType == true {
			pageType = "The end sign /f."
		}
		fmt.Printf("\n[ArgsStart]\n")
		fmt.Printf("startPage: %d\nendPage: %d\ninputFile: %s\npageLength: %d\npageType: %s\nprintDestation: %s\n[ArgsEnd]", args.startPage, args.endPage, args.inFileName, args.pageLen, pageType, args.printDest)
	}

}

func ErrorCheck(err error, object string) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "\n[Error]%s:", object)
		panic(err)
	}
}

3、參數檢查結束之後,程序開始調用excutecmd函數執行命令。

func excutecmd(args *selpg_args) {
	var fin *os.File
	if args.inFileName == "" {
		fin = os.Stdin
	} else {
		checkFileAccess(args.inFileName)
		var err error
		fin, err = os.Open(args.inFileName)
		ErrorCheck(err, "File input")
	}

	if len(args.printDest) == 0 {
		output2Des(os.Stdout, fin, args.startPage, args.endPage, args.pageLen, args.pageType)
	} else {
		output2Des(cmdExec(args.printDest), fin, args.startPage, args.endPage, args.pageLen, args.pageType)
	}
}

4、在-d參數存在時,涉及到了os/exec包的使用。

func cmdExec(printDest string) (*exec.Cmd, io.WriteCloser) {
	cmd := exec.Command("lp", "-d"+printDest)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	fout, err := cmd.StdinPipe()
	ErrorCheck(err, "Input pipe open\n")
	return cmd, fout
}

5、輸出函數output2Des將輸入的文件,按頁碼要求讀取並輸出到fout中。

func output2Des(printDest string, fin *os.File, pageStart int, pageEnd int, pageLen int, pageType bool) {

	lineCount := 0
	pageCount := 1
	buf := bufio.NewReader(fin)

	var cmd *exec.Cmd
	var fout io.WriteCloser
	if len(printDest) > 0 {
		cmd, fout = cmdExec(printDest)
	}

	for true {

		var line string
		var err error
		if pageType {
			//If the command argument is -f
			line, err = buf.ReadString('\f')
			pageCount++
		} else {
			//If the command argument is -lnumber
			line, err = buf.ReadString('\n')
			lineCount++
			if lineCount > pageLen {
				pageCount++
				lineCount = 1
			}
		}

		if err == io.EOF {
			break
		}
		ErrorCheck(err, "file read in\n")

		if (pageCount >= pageStart) && (pageCount <= pageEnd) {
			var outputErr error
			if len(printDest) == 0 {
				_, outputErr = fmt.Fprintf(os.Stdout, "%s", line)
			} else {
				_, outputErr = fout.Write([]byte(line))
				ErrorCheck(outputErr, "pipe input")
			}
			ErrorCheck(outputErr, "Error happend when output the pages.")
		}
	}

	if len(printDest) > 0 {
		fout.Close()
		errStart := cmd.Run()
		ErrorCheck(errStart, "CMD Run")
	}

	if pageCount < pageStart {
		fmt.Fprintf(os.Stderr, "\n[Error]: startPage (%d) greater than total pages (%d), no output written\n", pageStart, pageCount)
		os.Exit(9)
	} else if pageCount < pageEnd {
		fmt.Fprintf(os.Stderr, "\n[Error]: endPage (%d) greater than total pages (%d), less output than expected\n", pageEnd, pageCount)
		os.Exit(10)
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章