CLI(Command Line Interface)实用程序是Linux下应用开发的基础。正确的编写命令行程序让应用与操作系统融为一体,通过shell或script使得应用获得最大的灵活性与开发效率。Linux提供了cat、ls、copy等命令与操作系统交互;go语言提供一组实用程序完成从编码、编译、库管理、产品发布全过程支持;容器服务如docker、k8s提供了大量实用程序支撑云服务的开发、部署、监控、访问等管理任务;git、npm等都是大家比较熟悉的工具。尽管操作系统与应用系统服务可视化、图形化,但在开发领域,CLI在编程、调试、运维、管理中提供了图形化程序不可替代的灵活性与效率。
几乎所有语言都提供了完善的 CLI 实用程序支持工具。以下是一些入门文档(c 语言):
如果你熟悉 python :
阅读以后你应该知道 POSIX/GNU 命令行接口的一些概念与规范。命令行程序主要涉及内容:
使用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())
}
中文参考:
更多代码实践:
要求:使用 golang 开发 开发 Linux 命令行实用程序 中的 selpg
selpg命令格式:
-s:startPage,后面接开始读取的页号
-e:endPage,后面接结束读取的页号
-l:后面跟行数,代表多少行分为一页
-f:该标志无参数,代表按照分页符’\f’ 分页,一般默认72行为一页
-d:“-dDestination”选项将选定的页直接发送至打印机,“Destination”应该是 lp 命令“-d”选项可接受的打印目的地名称
input_file,output_file 2,error_file:输入文件、输出文件、错误信息文件的名字
将项目分为两部分:
1.引入所需包
import (
"bufio"
"github.com/spf13/pflag"
"fmt"
"io"
"math"
"os"
"os/exec"
)
这里采用的是pflag解析命令行参数,若要import pflag包,要先在本地安装spf13/pflag:
go get github.com/spf13/pflag
pflag基本的使用和“flag包”基本相同
新增:
// func IntP(name, shorthand string, value int, usage string) *int
// IntP is like Int, but accepts a shorthand letter that can be used after a single dash.
var ip= flag.IntP("flagname", "f", 1234, "help message")
var ip = flag.IntP("flagname", "f", 1234, "help message")
flag.Lookup("flagname").NoOptDefVal = "4321"
2.创建命令行参数结构体:包括开始页面、结束页面、操作文件名、操作页面长度等数据
type selpg_args struct {
startPage int
endPage int
inFile string
pageLen int
pageType bool //ture:-f类型, false:-l类型
printDestination string
}
3.main函数
func main() {
args := new(selpg_args)
getArgs(args)
check(args)
process(args)
}
4.getArgs()函数:命令处理函数,得到相应参数
func getArgs(args *selpg_args) {
//提示信息,如果给出的参数不正确或者需要查看帮助 -help,那么会给出这里指定的字符串
pflag.Usage = usage
pflag.IntVarP(&(args.startPage), "start", "s", 0, "start page")
pflag.IntVarP(&(args.endPage), "end", "e", 0, "end page")
pflag.IntVarP(&(args.pageLen), "line", "l", 72, "page len")
pflag.StringVarP(&(args.printDestination), "destionation", "d", "", "print destionation")
pflag.BoolVarP(&(args.pageType), "type", "f", false, "type of print")
pflag.Parse()
//从第一个不能解析的参数开始,后面的所有参数都是无法解析的。即使后面的参数中含有预定义的参数
//其他参数
othersArg := pflag.Args()
if len(othersArg) > 0 {
args.inFile = othersArg[0]
} else {
args.inFile = ""
}
}
5.check()函数:进行简单的命令的逻辑错误检查,比如说开始、结束页面为0或负数,开始页面在结束页面之后等
func check(args *selpg_args) {
if args.startPage == -1 || args.endPage == -1 {
os.Stderr.Write([]byte("you should input -s -e at least\n"))
os.Exit(0)
}
if args.startPage < 1 {
os.Stderr.Write([]byte("invalid start page\n"))
os.Exit(1)
}
if args.endPage < 1 || args.endPage < args.startPage {
os.Stderr.Write([]byte("invalid end page\n"))
os.Exit(2)
}
if args.pageLen < 1 {
os.Stderr.Write([]byte("invalid page length\n"))
os.Exit(3)
}
}
6.process()函数:根据命令打开相应文件,并对相应文件进行文件操作
func process(args *selpg_args) {
......
if args.inFile != "" {
fin, err1 = os.Open(args.inFile)
if err1 != nil {
fmt.Fprintf(os.Stderr, "Can not open input file \"%s\"\n", args.inFile)
os.Exit(11)
}
}
......
rd := bufio.NewReader(fin)
writer := bufio.NewWriter(os.Stdout)
if args.pageType == true {
process_f(rd, writer, args, &page_ctr)
} else {
process_l(rd, writer, args, &page_ctr, &line_ctr)
}
......
fin.Close()
fout.Close()
}
process()子函数,这里以process_l()函数为例:
func process_l(reader *bufio.Reader, writer *bufio.Writer, args *selpg_args, pageCtr *int, lineCtr *int) {
*lineCtr = 0
*pageCtr = 1
for {
line, err := reader.ReadBytes('\n')
if err != nil {
//遇到任何错误立即返回,并忽略 EOF 错误信息
if err == io.EOF {
break
}
os.Stderr.Write([]byte("read bytes from Reader error\n"))
os.Exit(5)
}
*lineCtr++
if *lineCtr > args.pageLen{
*lineCtr = 0
*pageCtr++
}
if *pageCtr >= args.startPage && *pageCtr <= args.endPage {
_, errW := writer.Write(line)
if errW != nil {
os.Stderr.Write([]byte("Write to file fail\n"))
os.Exit(6)
}
writer.Flush()
}
}
}
说明:test.txt为测试文档
selpg -s1 -e1 test.txt(默认显示72行)
selpg -s1 -e1 -l12 test.txt
selpg -s1 -e1 < test.txt
selpg -s1 -e1 -l12 test.txt >out.txt
selpg -s1 -e1 -l12 test.txt>error.txt
(这里无错误输出)
selpg -s1 -e1 -l12 test.txt >/dev/null
selpg -s1 -e0 -l12 test.txt >/dev/null
ps | selpg -s1 -e1 -l12 test.txt
selpg -s1 -e1 -l12 test.tx | cat -n