Golang CLI程序构建学习
作业要求,以及学习参考资料
https://pmlpml.gitee.io/service-computing/post/ex-cli-basic/
本次作业是模仿一个比较简单的CLI程序(基于C语言),用golang重写一遍
原CLI创建要求及其 学习
参考https://www.ibm.com/developerworks/cn/linux/shell/clutil/index.html
在此之前,需要了解一些基础知识
CLI程序:可以在终端中直接直接调用的程序
flag的作用:在CLI程序中,会有许多可选择的参数,例如 “selpg -s 100 -e 200”等,利用flag,可以直接获取s,和e参数的值
必选参数:例如 “gcc code.c” 必须要输入的文件等等
为啥要用Pflag,而不是用flag呢?(当然,我也没有很搞懂,但是它们的使用方法是类似的)
pflag 包与 flag 包的工作原理甚至是代码实现都是类似的,下面是 pflag 相对 flag 的一些优势:
- 支持更加精细的参数类型:例如,flag 只支持 uint 和 uint64,而 pflag 额外支持 uint8、uint16、int32 等类型。
- 支持更多参数类型:ip、ip mask、ip net、count、以及所有类型的 slice 类型。
- 兼容标准 flag 库的 Flag 和 FlagSet:pflag 更像是对 flag 的扩展。
- 原生支持更丰富的功能:支持 shorthand、deprecated、hidden 等高级功能。
除此之外,还需要了解 io 与 bufio的关系,用于文件的读写
go的io库里,读写文件的方法很难用,因此需要把io封装在bufio中。使用bufio的方法,可以更加方便
对于io和bufio的学习,参考https://blog.csdn.net/houyanhua1/article/details/88760853
最后,还需要学习os/exec中的 exec.Command(”命令名字“,“参数”),用来执行可能需要的打印命令
https://www.godoc.org/os/exec#example-Cmd-StdinPipe
通过官方文档的学习,了解到exec.Command()会返回*Cmd文件,可以用它来控制命令的输入和输出位置
代码简析:
CLI 命令行参数,存储的位置

main函数,先获取CLI命令行参数,然后检查是否要把内容送去打印机,确定输出位置。最后运行t检索input内容,输出。

检查是否有打印地址参数


用Pflag和os.Args获取命令行参数

处理input内容函数

使用selpg,根据要求网页的 使用selpg
每一个数字是一行










每一个数字是一页

由于没有默认打印机,所以出现了报错


完整代码:
package main
import flag "github.com/spf13/pflag"
import (
"fmt"
"os"
"io"
"bufio"
"os/exec"
)
var (
h bool
start_page, end_page, page_len int
//differ page ends for limited line or \f
page_type bool
print_dest, in_fileName string
)
var (
writer io.WriteCloser
)
func main(){
process_args()
//
writer = os.Stdout
if print_dest != "" {
t_cmd := exec.Command("lp", "-d" + print_dest)
var err error
if writer, err = t_cmd.StdinPipe(); err != nil {
fmt.Fprintf(os.Stderr, "could not open pipe")
return
}
t_cmd.Stdout = os.Stdout
t_cmd.Stderr = os.Stderr
if err := t_cmd.Start(); err != nil {
fmt.Fprintf(os.Stderr, "cmd start error")
return
}
}
if h {
flag.Usage()
return
}
process_input()
}
func process_args(){
in_fileName = os.Args[len(os.Args)-1]
flag.BoolVarP(&h, "help","h", false, "this help")
flag.IntVarP(&start_page, "start_page", "s", -1, "input the start page number")
flag.IntVarP(&end_page, "end_page", "e", -1, "input the end page number")
flag.IntVarP(&page_len, "page_len", "l", 72, "input the page length")
flag.BoolVarP(&page_type, "page_type", "f", false, "input the page type -f page end with \\f")
flag.StringVarP(&print_dest, "print_dest", "d", "", "input the print destination")
flag.Parse();
flag.Usage = usage
}
func process_input() {
// check the start page and the end page
if start_page == -1 || start_page < 0{
fmt.Fprintf(os.Stderr,"start page must be set")
return
}else if end_page == -1 || end_page < 0{
fmt.Fprintf(os.Stderr,"end page must be set")
return
}else if start_page > end_page {
fmt.Fprintf(os.Stderr,"start page must less or equal to the end page")
return
}
file, err := os.Open(in_fileName)
rFile := bufio.NewReader(file)
rFile = bufio.NewReader(file)
if (err != nil) {
//fmt.Println("a correct file name is necessary, open file Error! :", err)
rFile = bufio.NewReader(os.Stdin)
}
current_page := 1
if (page_type) {
for page, err := rFile.ReadBytes('\f'); current_page <= end_page; page, err = rFile.ReadBytes('\f') {
if err == io.EOF {
fmt.Println("the file doesn't have enough pages")
}
if current_page >= start_page {
writer.Write(page)
}
current_page += 1
}
}else {
lineNum := 1
for line, err := rFile.ReadBytes('\n'); current_page <= end_page; line, err = rFile.ReadBytes('\n') {
if err == io.EOF {
fmt.Println("the file doesn't have enough pages")
}
if current_page >= start_page {
writer.Write(line)
}
lineNum = lineNum + 1
if (lineNum == page_len + 1) {
current_page += 1
lineNum = 1
}
}
}
}
func usage() {
fmt.Fprintf(os.Stderr, "selpg Usage: selpg [-h] [-s start_page] [-e end_page] [-l page_len] [-f page_type] in_fileName\nOptions\n")
flag.PrintDefaults()
}

浙公网安备 33010602011771号