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参数的值

对于flag的学习,参考http://blog.studygolang.com/2013/02/%E6%A0%87%E5%87%86%E5%BA%93-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90flag/

必选参数:例如 “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

每一个数字是一行

 

 

 

 

 

 

 

 

 

 

每一个数字是一页

 

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

 

 

完整代码:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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()
}

 

posted @   woodx  阅读(282)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示