服务计算 - 3 Golang开发Linux命令行实用程序 - selpg

Golang开发Linux命令行实用程序 - selpg

1. 介绍

2. 设计与实现

2.1 设计思路

  • 理解selpg命令的功能以及处理流程。

  • selpg命令涉及的基本操作

    • 文件的读写
    • 从终端获取输入以及在终端输出

2.2 功能模块划分与实现

  • 构建命令行参数的结构体,由于参数使用的比较多,可以使用一个结构体存储起来,使用起来比较方便

    type selpg_args struct {
    	startPage      int
    	endPage        int
    	inFile         string
    	pageLen        int
    	pageType       bool // true for -f, false for -lNumber
    	outDestination string
    }
    
  • 参数解析

    • 使用pflag包对命令行输入参数进行解析
    • 使用 pflag 替代 goflag 以满足 Unix 命令行规范, 参考:Golang之使用Flag和Pflag
    • 获得flag参数后pflag.Parse()函数才能把参数解析出来
    • 使用pflag.Args()来获取未被标记的参数
    // 解析获取参数
    func getArgs(args *selpg_args) {
      pflag.IntVarP(&(args.startPage), "startPage", "s", -1, "start page")
      pflag.IntVarP(&(args.endPage), "endPage", "e", -1, "end page")
      pflag.IntVarP(&(args.pageLen), "pageLen", "l", 72, "the length of page")
      pflag.BoolVarP(&(args.pageType), "pageType", "f", false, "page type")
      pflag.StringVarP(&(args.outDestination), "outDestination", "d", "", "print destination")
      pflag.Parse()
    
      other := pflag.Args() // 其余参数
      if len(other) > 0 {
        args.inFile = other[0]
      } else {
        args.inFile = ""
      }
    }
    
  • 参数检查

    • 检查输入参数的合法性

      • 是否输入了起始页和结束页
      • 起始页大于1小于结束页以及不能溢出(MaxInt32
      • 结束页大于起始页并且不能溢出(MaxInt32
    • 遇到不合法则输出错误同时结束程序

      // 检查参数合法性
      func checkArgs(args *selpg_args) {
        if args.startPage == -1 || args.endPage == -1 {
          os.Stderr.Write([]byte("You shouid input like selpg -sNumber -eNumber ... \n"))
          os.Exit(0)
        }
      
        if args.startPage < 1 || args.startPage > math.MaxInt32 {
          os.Stderr.Write([]byte("You should input valid start page\n"))
          os.Exit(0)
        }
      
        if args.endPage < 1 || args.endPage > math.MaxInt32 || args.endPage < args.startPage {
          os.Stderr.Write([]byte("You should input valid end page\n"))
          os.Exit(0)
        }
      
        if (!args.pageType) && (args.pageLen < 1 || args.pageLen > math.MaxInt32) {
          os.Stderr.Write([]byte("You should input valid page length\n"))
          os.Exit(0)
        }
      }
      
  • 执行命令

    • 执行命令部分主要涉及到文件的读取以及写入或者标准输入的获取以及命令行下输出,我们使用到bufio包,至于-dXXX 实现,使用到 os/exec包

    • bufio包介绍与使用

      • bufio包实现了带缓存的 I/O 操作,它封装一个 io.Readerio.Writer 对象,使其具有缓存和一些文本读写功能

        • func NewReaderSize(rd io.Reader, size int) *Reader

          NewReaderSizerd 封装成一个带缓存的 bufio.Reader 对象,缓存大小由 size 指定(如果小于 16 则会被设置为 16)。如果rd的基类型就是有足够缓存的 bufio.Reader 类型,则直接将rd 转换为基类型返回。

        • func NewReaderSize(rd io.Reader, size int) *Reader

          NewReader相当于 NewReaderSize(rd, 4096)

        • func (reader *Reader) ReadBytes(delim byte) (line []byte, err error)

          ReadBytesreader中查找delim并读出 delim 及其之前的所有数据。如果 ReadBytes 在找到 delim 之前遇到错误,则返回遇到错误之前的所有数据,同时返回遇到的错误(通常是 io.EOF)。 只有当 ReadBytes 找不到 delim时,err 才不为 nil

    • os/exec包介绍与使用

      • os/exec包执行外部命令。它包装了os.StartProcess函数以便更容易的修正输入和输出,使用管道连接I/O,以及作其它的一些调整。

        • func Command(name string, arg ...string) *Cmd

          command返回cmd结构来执行带有相关参数的命令,它仅仅设定cmd结构中的Path和Args参数,如果name参数中不包含路径分隔符,command使用LookPath来解决路径问题,否则的话就直接使用name;Args直接跟在command命令之后,所以在Args中不许要添加命令。我们用该命令创建一个命令对象,参数为子进程路径和子进程参数(可选)

          // 指定运行的程序,实现模拟的打印机
          cmd := exec.Command("./" + args.outDestination)
          
        • func (c *Cmd) StderrPipe() (io.ReadCloser, error)

          StderrPipe返回一个pipe,这个管道连接到command的标准错误,当command命令退出时,Wait将关闭这些pipe

        • func (c *Cmd) StdinPipe() (io.WriteCloser, error)

          StdinPipe返回一个连接到command标准输入的管道pipe。我们可以通过在此处写入传输信息,然后作为子进程的标准输入。

      • 对于-dXXX的实现,创建一个子进程,让其模拟打印机,使用管道将数据传输给子进程,子进程读取管道内传输的信息并且打印出来。

    • 处理流程

      • 判断是否指定输入文件,为空则将标准输入作为输入,否则为文件流

      • 判断是否为-d类型

      • 判断输入类型为-l还是-f,依据要求读取输入,在输出到标准输出

      • 判断起始页数以及结束页数是否满足实际标准

        // 执行命令
        func processInput(args *selpg_args) {
        
          // read the file
          var reader *bufio.Reader
        
          if args.inFile == "" {
            reader = bufio.NewReader(os.Stdin)
          } else {
            fileIn, err := os.Open(args.inFile)
            defer fileIn.Close()
            if err != nil {
              os.Stderr.Write([]byte("Open file error\n"))
              os.Exit(0)
            }
            reader = bufio.NewReader(fileIn)
          }
        
          // output the file
          if args.outDestination == "" {
            // 输出到当前命令行
            outputCurrent(reader, args)
          } else {
            // 输出到目的地
            outputToDest(reader, args)
          }
        }
        
        // 输出到当前命令行
        func outputCurrent(reader *bufio.Reader, args *selpg_args);
        
        // 输出到指定目的地
        func outputToDest(reader *bufio.Reader, args *selpg_args);
        
    • 具体代码参见Github项目

4 参考文献

猜你喜欢

转载自blog.csdn.net/lllllyt/article/details/83017295