2020-10-12

CLI命令行实用程序开发

1. 实验准备

1. CLI概述

CLI(Command Line Interface)实用程序是Linux下应用开发的基础。正确的编写命令行程序让应用与操作系统融为一体,通过shell或script使得应用获得最大的灵活性与开发效率。例如:

Linux提供了cat、ls、copy等命令与操作系统交互;

go语言提供一组实用程序完成从编码、编译、库管理、产品发布全过程支持;

容器服务如docker、k8s提供了大量实用程序支撑云服务的开发、部署、监控、访问等管理任务;

git、npm等也是大家比较熟悉的工具。

尽管操作系统与应用系统服务可视化、图形化,但在开发领域,CLI在编程、调试、运维、管理中提供了图形化程序不可替代的灵活性与效率。

2. 基础知识

本次作业提供了Linux命令行程序的C语言文档:

开发命令行实用程序

Linux命令行程序设计

3. os库的使用

os库学习

本次作业将会用到的os中一些函数:

os.Exit(int): 用于程序退出,不同的参数代表不同的退出情况

os.Stdin: 标准输入,selpg默认使用Stdin

os.Stdout: 标准输出,selpg默认使用Stdout

os.Open(string): 用于打开只读文件,返回值是文件指针和错误信息

os.OpenFile(string): 高级版的打开文件,可以设置打开文件的方式,文件的操作形式。返回值是文件指针和错误信息。

os.Close(string): 用于关闭文件

os.O_WRONLY: 只读文件标识符

os.ModeAppend: Append模式

4. pflag库学习

Golang之引用Flag和Pflag

1. 首先要让go获取pflag,终端运行命令:go get github.com/spf13/pflag
2. 在程序中引用这个包:import flag "github.com/spf13/pflag"
3. 使用pflag库
(1) 对flag进行绑定。

当你输入命令的时候,flag就会自动识别命令并为你绑定好的变量进行赋值。

以第一条命令为例:

第一个参数就是需要绑定的指针

第二个参数是命令中的对应的表示,比如上面的第一行代码,就会在输入–s 123的时候,将123这个值赋值给sa.startPage。

第三个参数是s的初始值,若是你没有对s进行赋值,那么s就会维持这个初始值。

第四个参数是命令提示,当你使用–h查询命令含义的时候,这串字符就会对应的出现在每个命令参数后面。

(2) flag.Parse()

用于对命令行参数进行解析。这个过程就是刚刚所说的赋值过程,比如你输入以下命令:
selpg -s1 -e2 -l66

​然后执行了 flag.Parse() ,那么flag就会自动将1赋值给s,2赋值给e,66赋值给l。

(3) flag.PrintDefaults()

用于输出帮助信息,就如前面提到的,要是你不知道各命令行参数是什么意思,调用这个函数就会将刚刚绑定flag的第四个参数中的字符床显示在屏幕中。

(4) flag.NArg()

返回没有flag与之对应的参数的数量

(5) flag.Arg(int)

访问指定位置的non-flag参数,即没有flag与之对应的参数

2. 程序设计

$GOPATH/src下分别创建selpglp1文件夹,然后在selpg下创建selpg.go,lp1下创建lp1.go

1. selpg.go文件

导入所需包
import(
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"github.com/spf13/pflag"
)
定义结构体
type selpg_args struct{
	startPage      int  //开始页
	endPage        int //结束页
	input         string //输入文件
	pageLen        int //页长(页行数)
	pageType       bool  //两种类型,true表示-f,false表示-lnumber
	output string // 输出文件
} 

main函数
func main(){
	var sa selpg_args
	ArgsInit(&sa)
	Input(&sa)
}
ArgsInit函数
func ArgsInit(sa *selpg_args){
	pflag.IntVarP(&(sa.startPage),"startPage","s",-1,"start page")
	pflag.IntVarP(&(sa.endPage),"endPage","e",-1,"end page")
	pflag.IntVarP(&(sa.pageLen),"pageLen","l",72,"the length of page")
	pflag.BoolVarP(&(sa.pageType),"pageType","f",false,"page type")
	pflag.StringVarP(&(sa.output),"output","d","","print destination")
	pflag.Parse()
	sa_left:=pflag.Args() // 其余参数
	if len(sa_left) > 0 {
		sa.input=sa_left[0]
	} else {
		sa.input=""
	}
	check(sa)
}
check函数
func check(sa *selpg_args){
	if sa==nil{
		fmt.Fprintf(os.Stderr,"\n[Error]The args is nil! Please check your program!\n\n")
		os.Exit(0)
	}else if (sa.startPage==-1)||(sa.endPage==-1){
		fmt.Fprintf(os.Stderr,"\n[Error]The startPage and endPage do not allowed empty! Please check your command.\n\n")
		os.Exit(0)
	}else if sa.startPage>sa.endPage{
		fmt.Fprintf(os.Stderr,"\n[Error]The startPage can not be bigger than the endPage! Please check your command!\n\n")
		os.Exit(0)
	}else if (sa.startPage<1)||(sa.endPage<1){
		fmt.Fprintf(os.Stderr,"\n[Error]The startPage and endPage do not exist! Please check your command.\n\n")
		os.Exit(0)
	}
	if sa.pageType==false&&sa.pageLen<1 {
		fmt.Fprintf(os.Stderr,"\n[Error]You should input valid page length!\n\n")
		os.Exit(0)
	}
}
Input函数
func Input(sa *selpg_args){
	var reader *bufio.Reader
	var cmd *exec.Cmd=nil
	var stdin io.WriteCloser=nil
	writer:=bufio.NewWriter(os.Stdout)
	if sa.input=="" {
		reader=bufio.NewReader(os.Stdin)
	} else{
		fileIn,err:=os.Open(sa.input)
		defer fileIn.Close()
		if err!=nil {
			os.Stderr.Write([]byte("Open file error.\n"))
			os.Exit(0)
		}
		reader=bufio.NewReader(fileIn)
	}
	
	if sa.output!="" {
		cmd=exec.Command(sa.output)
		var pipeErr error;
		stdin,pipeErr=cmd.StdinPipe()
		if pipeErr!=nil {
			fmt.Println(pipeErr)
			os.Exit(0)
		}
		startErr:=cmd.Start()
		if startErr!=nil {
			fmt.Println(startErr)
			os.Exit(0)
		}
	}
	lineCtr:=0
	pageCtr:=1
	endSign:='\n'
	if sa.pageType==true {
		endSign='\f'
	}
	for{
		strLine,errRead:=reader.ReadBytes(byte(endSign))
		if errRead!=nil {
			if errRead==io.EOF {
				writer.Flush()
				break
			} else{
				os.Stderr.Write([]byte("Read bytes from reader failed.\n"))
				os.Exit(0)
			}
		}
		if pageCtr>=sa.startPage&&pageCtr<=sa.endPage {
			_,errWrite:=writer.Write(strLine)
			if errWrite!=nil {
				fmt.Println(errWrite)
				os.Stderr.Write([]byte("Write bytes to out failed.\n"))
				os.Exit(0)
			}
			if stdin!=nil {
				_,errWrite:=stdin.Write(strLine)
				if errWrite!=nil {
					fmt.Println(errWrite)
					os.Stderr.Write([]byte("Write bytes to out failed.\n"))
					os.Exit(0)
				}
			}
		}
		if sa.pageType==true {
			pageCtr++
		} else{
			lineCtr++
		}
		if sa.pageType==false&&lineCtr==sa.pageLen {
			lineCtr=0
			pageCtr++
		}
		if pageCtr>sa.endPage {
			writer.Flush()
			break
		}
	}
	if stdin!=nil {
		stdin.Close()
	}
	if cmd!=nil {
		if err:=cmd.Wait();err!=nil {
			fmt.Println(err)
			os.Exit(0)
		}
	}
}

2. lp1.go文件

package main
import(
	"bufio"
	"io"
	"os"
)
func main(){
	reader:=bufio.NewReader(os.Stdin)
	writer:=bufio.NewWriter(file)
	file,openErr:=os.OpenFile("./lp1.txt",os.O_RDWR|os.O_CREATE|os.O_APPEND,0644)
	if openErr!=nil {
		panic(openErr)
	}
	for{
		line,errRead:=reader.ReadBytes('\n')
		if errRead!=nil {
			if errRead==io.EOF {
				break
			} else {
				os.Stderr.Write([]byte("Fail to read bytes from reader.\n"))
				os.Exit(0)
			}
		}
		_,errWrite:=writer.Write(line)
		if errWrite!=nil {
			os.Stderr.Write([]byte("Fail to write bytes to file.\n"))
			os.Exit(0)
		}
		writer.Flush()
	}
}

3. input.txt,output.txterror.txt文件(自己准备)

3. 功能测试

在$GOPATH/src/selpg下终端执行以下命令:

go build selpg.go
go install selpg

在$GOPATH/src/lp1下终端执行以下命令:

go build lp1.go
go install lp1

1. $ selpg -s1 -e1 input_file

在这里插入图片描述

2. $ selpg -s1 -e1 < input_file

在这里插入图片描述

3. other_command | selpg -s10 -e20

在这里插入图片描述

4. $ selpg -s10 -e20 input_file >output_file

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5. $ selpg -s10 -e20 input_file 2>error_file

在这里插入图片描述
在这里插入图片描述

6. $ selpg -s10 -e20 input_file >output_file 2>error_file

在这里插入图片描述
在这里插入图片描述

7. $ selpg -s10 -e20 input_file >output_file 2>/dev/null

在这里插入图片描述
在这里插入图片描述

8. $ selpg -s10 -e20 input_file >/dev/null

在这里插入图片描述

9. $ selpg -s10 -e20 input_file | other_command

在这里插入图片描述
在这里插入图片描述

10. $ selpg -s10 -e20 input_file 2>error_file | other_command

在这里插入图片描述

11.

在这里插入图片描述

12.

13.

在这里插入图片描述

14.

在这里插入图片描述

4. 单元或集成测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值