CLI(Command Line Interface)實用程序是Linux下應用開發的基礎。正確的編寫命令行程序讓應用與操做系統融爲一體,經過shell或script使得應用得到最大的靈活性與開發效率。Linux提供了cat、ls、copy等命令與操做系統交互;go語言提供一組實用程序完成從編碼、編譯、庫管理、產品發佈全過程支持;容器服務如docker、k8s提供了大量實用程序支撐雲服務的開發、部署、監控、訪問等管理任務;git、npm等都是你們比較熟悉的工具。儘管操做系統與應用系統服務可視化、圖形化,但在開發領域,CLI在編程、調試、運維、管理中提供了圖形化程序不可替代的靈活性與效率。html
參閱 Selpg命令行程序設計邏輯,實現一個selpg頁選擇程序,知足selpg設計要求。linux
程序按照讀取參數、判斷參數是否合規、讀取文件、肯定輸出位置並輸出順序執行。當發現錯誤時拋出錯誤並終止流程。git
selpg所需參數有必須的開始頁碼-s以及結束頁碼-e,可選的輸入文件名、自定頁長-l、遇換頁符換頁-f和輸出地址。其中自定頁長和遇換頁符換頁兩個選項是互斥的,不能同時使用。github
定義保存參數數據的結構體golang
type selpgArgs struct { startPage int endPage int inFileName string pageLen int pageType bool printDest string }
輸入參數使用 github.com/spf13/pflag 包提供的pflag進行處理,pflag包於flag用法相似,但pflag相對於flag可以更好地知足 Unix 命令行規範。參考:Golang pflagdocker
func getArgs(args *selpgArgs) { 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 = "" } }
func XXXVarP(p *XXX, name, shorthand string, value XXX, usage string)
命令行參數獲取以後,首先要進行參數檢查以儘可能避免參數謬誤。出現錯誤時輸出問題並正常結束程序。參數正確則將各個參數值輸出到屏幕上。shell
func checkArgs(args *selpgArgs) { 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 !\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) } }
參數檢查結束以後,程序開始調用excuteCMD函數執行命令。npm
func checkError(err error, object string) { if err != nil { fmt.Fprintf(os.Stderr, "\n[Error]%s:", object) panic(err) } } func excuteCMD(args *selpgArgs) { var fin *os.File if args.inFileName == "" { fin = os.Stdin } else { checkFileAccess(args.inFileName) var err error fin, err = os.Open(args.inFileName) checkError(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) } } func checkFileAccess(filename string) { _, errFileExits := os.Stat(filename) if os.IsNotExist(errFileExits) { fmt.Fprintf(os.Stderr, "\n[Error]: input file \"%s\" does not exist\n", filename) os.Exit(7) } }
在-d參數存在時,涉及到了os/exec包的使用,這裏能夠參考golang中os/exec包用法。編程
func cmdExec(printDest string) io.WriteCloser { cmd := exec.Command("lp", "-d"+printDest) fout, err := cmd.StdinPipe() checkError(err, "StdinPipe") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr errStart := cmd.Run() checkError(errStart, "CMD run") return fout }
輸出函數output2Des將輸入的文件,按頁碼要求讀取並輸出到fout中。緩存
func output2Des(fout interface{}, fin *os.File, pageStart int, pageEnd int, pageLen int, pageType bool) { lineCount := 0 pageCount := 1 buf := bufio.NewReader(fin) 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 } checkError(err, "file read in") if (pageCount >= pageStart) && (pageCount <= pageEnd) { var outputErr error if stdOutput, ok := fout.(*os.File); ok { _, outputErr = fmt.Fprintf(stdOutput, "%s", line) } else if pipeOutput, ok := fout.(io.WriteCloser); ok { _, outputErr = pipeOutput.Write([]byte(line)) } else { fmt.Fprintf(os.Stderr, "\n[Error]:fout type error. ") os.Exit(8) } checkError(outputErr, "Error happend when output the pages.") } } 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) } }
./selpg -s1 -e1 input_file.txt
./selpg -s1 -e1 input_file.txt
./selpg -s1 -e2 input_file.txt >output_file
./selpg -s1 -e4 input_file.txt 2>error_file
./selpg -s1 -e3 input_file.txt >output_file 2>error_file
./selpg -s1 -e2 -f input_file.txt
./selpg -s1 -e1 -dCups-PDF selpg.go