服务计算学习之路二——selpg命令行程序编写

服务计算学习之路二——selpg命令行程序编写

作业内容

实验前

说真的,不知是原作者的博文跨度大还是我的cli程序编写基础差(也确实没怎么接触过),看完原作者的介绍后,还是一头雾水,还好给了C语言源码,虽然有个别语句看不懂,但总体还是看懂了。简单来说,做的是一个帮助用户打印文件中特定的页的一个命令行小工具。

实验思路

参考原作者的思路:

  • 检查用户输入的命令行参数是否合法,如果不合法则输出错误,并给出程序的用法,终止程序
  • 如果合法则解析参数,赋值到相应的变量里,这个步骤里Go语言提供了flag包能更好地处理命令行参数问题,如果涉及到文件的问题,还要检查文件的路径是否正确,是否有权限读写之类的。
  • 最后执行程序。
    • 由实验思路来看似乎很简单,但对于刚入门Go不久并且又对cli程序不太了解的我还是有点吃力的,最后实现的demo也没有原作者的功能完全。

实验步骤

  • Step1:用pflag代替Go自带的flag包
    根据老师的要求,用pflag代替Go自带的flag包,其实这两个包的大部分功能都一样,用法也基本一样,不过这两个包对应的Unix命令行程序风格不一样,下载pflag包:
    go get github.com/spf13/pflag
    使用时建议用

    import(
    	flag "github.com/spf13/pflag"
    )
    

    这样就能使用flag相关的代码了,可以提高程序的可移植性。不过我一开始没知道能这样用,所以下面的程序中我还是用了pflag

  • Step2:创建selpg程序对应的变量

    const MAX_INT = int(^uint(0) >> 1)
    
    type selpgArgs struct {
    	// selpg -s startPage -e endPage [-l linePerPage | -f ][-d dest] filename
    	startPage  int    //开始页码
    	endPage    int    //结束页码
    	filename   string //文件名
    	pageLength int    // 每页长度
    	pageType   bool   // 页类型
    	printDest  string // 输出地址
    }
    

    用了一个结构体来定义。

  • Step3:用pflag包进行参数的绑定,关于flag包的一些教程:Golang之使用Flag和Pflag命令行参数解析flag

    /*InitArgs sss */
    func InitArgs(p *selpgArgs) {
    	pflag.Usage = Usage
    	pflag.IntVarP(&p.startPage, "start", "s", 1, "Start Page Number")
    	pflag.IntVarP(&p.endPage, "end", "e", 1, "End Page Number")
    	pflag.IntVarP(&p.pageLength, "LinePerPage", "l", 72, "Line in Each Page")
    	pflag.BoolVarP(&p.pageType, "printType", "f", false, "flag Splits Page")
    	pflag.StringVarP(&p.printDest, "printDest", "d", "", "Destinaton of Print")
    }
    

    这里我定义了一个函数实现对参数的绑定,模块化,这样,但输入的参数有-s-e等,这段代码会自动将后面带的参数绑定到对应的变量里另外,最好也绑定pflag.Usage:

    func Usage() {
    	fmt.Fprintf(os.Stderr, "Usage:\tselpg -s startPage -e endPage [-l linePerPage | -f ][-d dest] filename\n")
    	fmt.Fprintf(os.Stderr, "\t-s=Number\tStart Page Number\n")
    	fmt.Fprintf(os.Stderr, "\t-e=Number\tLine in Each Page\n")
    	fmt.Fprintf(os.Stderr, "\t-l=Number\tStart Page Number\n")
    	fmt.Fprintf(os.Stderr, "\t-f=BoolValue(true or false)\tflag Splits Page\n")
    	fmt.Fprintf(os.Stderr, "\t-d=string\tDestinaton of Print\n")
    	fmt.Fprintf(os.Stderr, "\t[filename]\t\n")
    }
    

    这个函数是在命令行参数为-h--help时输出的帮助菜单。

  • Step4:接下来就是检查参数的正确性与否,这一部分一般没什么问题:

    func ProcessArgs(ac int, selpg *selpgArgs) {
    	if ac < 3 {
    		fmt.Fprintf(os.Stderr, "Not enough arguments\n")
    		pflag.Usage()
    		os.Exit(1)
    	} else if os.Args[1][0] != '-' && os.Args[1][1] != 's' {
    		fmt.Fprintf(os.Stderr, "First arg should be -s=Number\n")
    		pflag.Usage()
    		os.Exit(2)
    	} else if selpg.startPage < 1 || selpg.startPage > (MAX_INT-1) {
    		fmt.Fprint(os.Stderr, "StartPage Number invalid\n")
    		pflag.Usage()
    		os.Exit(3)
    	} else if os.Args[3][0] != '-' && os.Args[3][1] != 'e' {
    		fmt.Fprintf(os.Stderr, "Second arg should be -e=Number\n")
    		pflag.Usage()
    		os.Exit(4)
    	} else if selpg.endPage < 1 || selpg.endPage > (MAX_INT-1) || selpg.startPage < selpg.endPage {
    		fmt.Fprintf(os.Stderr, "EndPage Number invalid\n")
    		pflag.Usage()
    		os.Exit(5)
    	} else if selpg.pageLength < 1 {
    		fmt.Fprintf(os.Stderr, "PageLength invalid\n")
    		pflag.Usage()
    		os.Exit(6)
    	}
    
    	if pflag.NArg() > 0 {
    		selpg.filename = pflag.Arg(0)
    		// check if the file exits
    		file, err := os.Open(selpg.filename)
    		if err != nil {
    			fmt.Fprintf(os.Stderr, "The filename doesn't exits\n")
    			os.Exit(7)
    		}
    		//reader = bufio.NewReader(file)
    		file.Close()
    	}
    }
    
  • Step5: 检查完参数后,都合法的话就执行对应的操作,注意一下文件的读和写就差不多了:

    func ProcessInput(selpg *selpgArgs) {
    	fout := os.Stdout
    	result := ""
    	pageCount := 0
    	lineCount := 0
    	reader := bufio.NewReader(os.Stdin)
    	if selpg.filename != "" {
    		file, err := os.Open(selpg.filename)
    		if err != nil {
    			fmt.Fprintf(os.Stderr, "Can not open the file\n")
    			os.Exit(8)
    		}
    		defer file.Close()
    		reader = bufio.NewReader(file)		
    	}
    
    	if selpg.printDest != "" {
    		file, err := os.OpenFile(selpg.printDest,os.O_RDWR,0777)
    		if err != nil {
    			fmt.Fprintf(os.Stderr, "Can not open the destination file!\n")
    			os.Exit(9)
    		}
    		defer file.Close()
    		fout = file
    	}
    	if selpg.pageType == true {
    		pageCount = 1
    		for {
    			str, err := reader.ReadString('\f')
    			if err == io.EOF || pageCount > selpg.endPage {
    				break
    			} else if err != nil {
    				fmt.Fprintf(os.Stderr, "Read File Error1!\n")
    				os.Exit(10)
    			}
    			pageCount++
    			fout.Write([]byte(str))
    			result = strings.Join([]string{result, str}, "")
    		}
    	} else {
    		pageCount = 1
    		lineCount = 0
    		for {
    			str, err := reader.ReadString('\n')
    			if err == io.EOF || pageCount > selpg.endPage {
    				break
    			} else if err != nil {
    				fmt.Fprintf(os.Stderr, "Read File Error2!\n")
    			}
    			lineCount++
    			if lineCount > selpg.pageLength {
    				lineCount = 1
    				pageCount++
    			}
    			fout.Write([]byte(str))
    			result = strings.Join([]string{result, str}, "")
    		}
    	}
    	if selpg.printDest != "" {
    		cmd := exec.Command("lp", "-d"+selpg.printDest)
    		cmd.Stdin = strings.NewReader(result)
    		var stderrOut bytes.Buffer
    		cmd.Stderr = &stderrOut
    		err := cmd.Run()
    		if err != nil {
    			fmt.Fprintf(os.Stderr, stderrOut.String())
    		}
    	}
    }
    

执行结果:

  • 执行语句:
  • ./selpg.exe -h
  • ./selpg.exe -s 1 -e 1 -d output.txt input.txt
    input.txt 内容:

  • 其它命令就不一一展示了

总结

虽然做出来的demo没有原作者的功能完美,但也从中学习了很多东西,也增强了对Go的学习和理解。

我的代码传送门
参考的博客:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值