欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

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

程序员文章站 2022-03-24 20:48:57
服务计算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)
    }

}

命令行参数解析FLAG

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参数少于三个,其余可能出现的错误较多,在此不再赘述
    CLI 命令行实用程序开发基础
  • selpg -s1 -e1 boke.mdselpg -s 1 -e 1 boke.mdselpg -s1 -e1 < boke.md三者等效
    CLI 命令行实用程序开发基础
  • pwd | selpg -s1 -e12将pwd的输出作为参数输入到selpg
    CLI 命令行实用程序开发基础
  • selpg -s1 -e1 boke.md > temp.txt读取并写入temp.txt
    CLI 命令行实用程序开发基础
    CLI 命令行实用程序开发基础
  • selpg -s1 -e1 boke.md -13读取三行
    CLI 命令行实用程序开发基础
  • selpg -s1 -e1 -f boke.md读取不定长的页,直到\f,由于boke.md中不含\f,所以一直读到结束,只截取部分图片如下
    CLI 命令行实用程序开发基础

参考博客
往届参考博客

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

相关标签: 服务计算