当前位置: 移动技术网 > 网络运营>服务器>Linux > CLI 命令行实用程序开发基础

CLI 命令行实用程序开发基础

2020年10月10日  | 移动技术网网络运营  | 我要评论
服务计算Homework03目的使用 golang 开发开发 Linux 命令行实用程序中的 selpg要求请按文档 使用 selpg 章节要求测试你的程序请使用 pflag 替代 goflag 以满足 Unix 命令行规范, 参考:Golang之使用Flag和Pflaggolang 文件读写、读环境变量,请自己查 os 包“-dXXX” 实现,请自己查 os/exec 库,例如案例Command,管理子进程的标准输入和输出通常使用 io.Pipe,具体案例见Pipe请自带测试程序,

服务计算Homework03

  • 项目地址
  • 使用说明:根据博客内容配置好环境后,在对应goPath/src中创建selpg/selpg.go,完成代码后在goPath/src/selpg中执行go build生成selpg文件,即可在当前路径下使用selpg xxx对应命令

目的

使用 golang 开发开发 Linux 命令行实用程序中的 selpg

要求

  • 请按文档使用 selpg 章节要求测试你的程序
  • 请使用 pflag 替代 goflag 以满足 Unix 命令行规范, 参考:Golang之使用Flag和Pflag
  • golang 文件读写、读环境变量,请自己查 os 包
  • “-dXXX” 实现,请自己查 os/exec 库,例如案例Command,管理子进程的标准输入和输出通常使用 io.Pipe,具体案例见Pipe
  • 请自带测试程序,确保函数等功能正确

环境配置

  1. Linux配置go语言环境
  2. 使用命令行apt install golang安装的go语言没有pflag库,手动安装命令为go get github.com/spf13/pflag,安装好后可以直接import

代码实现

  1. 参数结构体selpg_args

将page_type改为bool类型,因为pflag是要识别选项后面跟着的参数的,但-f后面不跟参数,如果用整数类型会报错,而布尔类型的一个特点是不能直接在选项后面跟参数,如果要跟参数,必须用一个等号连接,如selpg -s1 -e2 -f=true,如果出现-f也相当于-f=true,所以如果没有出现-f选项,则page_type为默认值false(对应定长72行数),如果出现-f选项,则page_type设为true(对应不定长行数)。其他与c语言版本相同

type selpg_args struct {
        start_page int 
        end_page int
        page_len int
        page_type bool
        in_filename string
        print_dest string
}
  1. 获取参数

由课件可得os、flag包的参数简单处理为:

package main

import (
    "fmt"
    "os"
)

func main() {
    for i, a := range os.Args[1:] {
        fmt.Printf("Argument %d is %s\n", i+1, a)
    }

}

package main

import (
    "flag" 
    "fmt"
)

func main() {
    var port int
    flag.IntVar(&port, "p", 8000, "specify port to use.  defaults to 8000.")
    flag.Parse()

    fmt.Printf("port = %d\n", port)
    fmt.Printf("other args: %+v\n", flag.Args())
}

仿写上述代码获取参数如下

func get_args(sa selpg_args) {
	progname = os.Args[0]
	flag.IntVarP(&sa.start_page, "start_page", "s", -1, "start page")
    flag.IntVarP(&sa.end_page, "end_page", "e", -1, "end page")
    flag.IntVarP(&sa.page_len, "page_len", "l", 15, "page len")
    flag.BoolVarP(&sa.page_type, "page_type", "f", false, "page_type")
    flag.StringVarP(&sa.print_dest, "print_dest", "d", "", "print destination")

    flag.Parse()
    if flag.NArg() > 0 {
        sa.in_filename = flag.Arg(0)
    }
}
  1. 检查参数
  • 对下列进行逐项检查
  • 参数个数
  • 开始页数、结束页数、每页行数越界
  • 开始页数>结束页数
  • 输入文件
func check_args(sa selpg_args) {
    if len(os.Args) < 3 {
        fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progname)
        usage()
        os.Exit(1)
    }
    if sa.start_page < 1 || sa.start_page > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid start page %d\n", progname, sa.start_page)
        usage()
        os.Exit(2)
    }
    if sa.end_page < 1 || sa.end_page > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid end page %d\n", progname, sa.end_page)
        usage()
        os.Exit(3)
	}
	if page_type == true && (sa.page_len < 1 || sa.page_len > INT_MAX) {
        fmt.Fprintf(os.Stderr, "%s: invalid page length %d\n", progname, sa.page_len)
        usage()
        os.Exit(4)
    }
    if sa.end_page < sa.start_page {
        fmt.Fprintf(os.Stderr, "%s: end page should not be less than start pag\n", progname)
        usage()
        os.Exit(3)
    }
    if sa.in_filename != "" {
        if _, err := os.Stat(sa.in_filename); os.IsNotExist(err) {
            fmt.Fprintf(os.Stderr, "%s: input file \"%s\" does not exist\n", progname, sa.in_filename)
			os.Exit(5);
        }
    }
}
  1. 读取输入

用缓冲区bufio.Reader进行输入,如果输入命令中不包含输入文件,则输入默认来自标准输入,否则尝试打开文件,将文件流作为输入

var reader *bufio.Reader
if sa.in_filename == "" {
    reader = bufio.NewReader(os.Stdin)
} else {
    fin, err := os.Open(sa.in_filename)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s: could not open input file \"%s\"\n", progname, sa.in_filename)
        os.Exit(6)
    }
    reader = bufio.NewReader(fin)
    defer fin.Close()
}
  1. 输出

采用管道输出,首先通过exec.Command创建了一个子进程,执行打印,打印的目的地为输入命令中的目的地,如果输入命令中没有目的地选项,则输出默认为标准输出。然后将程序的输出流writer设为打印进程的输入管道,实现将读取的内容通过管道写到目的文件中

var writer io.WriteCloser 
if sa.print_dest == "" {
    writer = os.Stdout
} else {
    cmd := exec.Command("lp","-d"+ sa.print_dest)
    var err error
    if writer, err = cmd.StdinPipe(); err != nil {
        fmt.Fprintf(os.Stderr, "%s: could not open pipe to \"%s\"\n", progname, sa.print_dest)
        fmt.Println(err)
        os.Exit(7)
    }
    cmd.Stdout = os.Stdout;
    cmd.Stderr = os.Stderr;
    if err = cmd.Start(); err != nil {
        fmt.Fprintf(os.Stderr, "%s: cmd start error\n", progname)
        fmt.Println(err)
        os.Exit(8)
    }
}
  1. 文件读写

基本套用c语言版本的思路,不同的是将两种不同页类型(定长和不定长)的读取统一在一个循环内。因为不定长页是以\f为结束符的,所以reader.ReadString每读一个\f,不定长页就增加一页

line_ctr, page_ctr, page_len := 1, 1, sa.page_len
ptFlag := '\n'
if sa.page_type {
    ptFlag = '\f'
    page_len = 1
}

for {
    line, crc := reader.ReadString(byte(ptFlag));
    if crc != nil && len(line) == 0 {
        break
    }
    if line_ctr > page_len {
        page_ctr++
        line_ctr = 1
    }
    if page_ctr >= sa.start_page && page_ctr <= sa.end_page {
        _, err := writer.Write([]byte(line))
        if err != nil {
            fmt.Println(err)
            os.Exit(9)
        }
    }
    line_ctr++
}

// 检查读取是否出错
if page_ctr < sa.start_page {
    fmt.Fprintf(os.Stderr, "\n%s: start_page (%d) greater than total pages (%d)," + " no output written\n", progname, sa.start_page, page_ctr)
} else if page_ctr < sa.end_page {
    fmt.Fprintf(os.Stderr, "\n%s: end_page (%d) greater than total pages (%d)," + " less output than expected\n", progname, sa.end_page, page_ctr)
}

结果展示

  • 写完代码在该文件目录下执行go build,产生一个可执行文件selpg
  • selpg参数少于三个,其余可能出现的错误较多,在此不再赘述
    在这里插入图片描述
  • selpg -s1 -e1 boke.mdselpg -s 1 -e 1 boke.mdselpg -s1 -e1 < boke.md三者等效
    在这里插入图片描述
  • pwd | selpg -s1 -e12将pwd的输出作为参数输入到selpg
    在这里插入图片描述
  • selpg -s1 -e1 boke.md > temp.txt读取并写入temp.txt
    在这里插入图片描述
    在这里插入图片描述
  • selpg -s1 -e1 boke.md -13读取三行
    在这里插入图片描述
  • selpg -s1 -e1 -f boke.md读取不定长的页,直到\f,由于boke.md中不含\f,所以一直读到结束,只截取部分图片如下
    在这里插入图片描述

参考博客
往届参考博客

本文地址:https://blog.csdn.net/try17875864815/article/details/108936677

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网