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
- 请自带测试程序,确保函数等功能正确
环境配置
- Linux配置go语言环境
- 使用命令行
apt install golang
安装的go语言没有pflag
库,手动安装命令为go get github.com/spf13/pflag
,安装好后可以直接import
代码实现
- 参数结构体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
}
- 获取参数
由课件可得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)
}
}
- 检查参数
- 对下列进行逐项检查
- 参数个数
- 开始页数、结束页数、每页行数越界
- 开始页数>结束页数
- 输入文件
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);
}
}
}
- 读取输入
用缓冲区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()
}
- 输出
采用管道输出,首先通过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)
}
}
- 文件读写
基本套用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.md
、selpg -s 1 -e 1 boke.md
、selpg -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
下一篇: P2077 红绿灯(模拟,洛谷,字符串)