服务计算:简单的CLI程序

Go Online传送门

Go Online

命令行程序是什么

CLI(Command Line Interface)实用程序是Linux下应用开发的基础。正确的编写命令行程序让应用与操作系统融为一体,通过shell或script使得应用获得最大的灵活性与开发效率。Linux提供了cat、ls、copy等命令与操作系统交互;go语言提供一组实用程序完成从编码、编译、库管理、产品发布全过程支持;容器服务如docker、k8s提供了大量实用程序支撑云服务的开发、部署、监控、访问等管理任务;git、npm等都是大家比较熟悉的工具。尽管操作系统与应用系统服务可视化、图形化,但在开发领域,CLI在编程、调试、运维、管理中提供了图形化程序不可替代的灵活性与效率。


selpg的命令格式

  • -s:startPage,后面接开始读取的页号
  • -e:endPage,后面接结束读取的页号
  • -l:后面跟行数,代表多少行分为一页
  • -f:该标志无参数,代表按照分页符’\f’ 分页,一般默认72行为一页
  • -d:“-dDestination”选项将选定的页直接发送至打印机,“Destination”应该是 lp 命令“-d”选项可接受的打印目的地名称
  • input_file,output_file 2,error_file:输入文件、输出文件、错误信息文件的名字

pflag的安装和使用

  • 安装plag

    go get github.com/spf13/pflag
    
  • pflag的基本使用

    • 定义flags
    // 返回的是 指针
    var ip = flag.Int("flagname", 1234, "help message for flagname")
    
    • 将flag绑定到一个变量
    var flagvar int
    
    func init() {
    	flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
    }
    
    • 绑定自定义的类型
    // 自定义类型需要实现value接口
    flag.Var(&flagVal, "name", "help message for flagname")
    
    • 解析
    // 解析函数将会在碰到第一个非flag命令行参数时停止
    flag.Parse()
    
    • 添加shorthand参数
    // 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"
    

其他包的使用

代码设计

阅读源码selpg.c, 根据类似的结构写

  • 创建结构体表示命令行参数

    type selpg_args struct{
    	spage int		//start page
    	epage int		//end page
    	plen int 		//page length
    	ptype int 		//page type, -f , default: -l
    	infile string 	//input file
    	printdest string //print destination
    }
    
  • main函数

    func main() {
    	selArgs := selpg_args{}
    	programName = os.Args[0]
    
    	initArgs(&selArgs)				//init the flag arguments, setting default values, and get arguments from the command line
    	handleArgs(len(os.Args), &selArgs) //handle the arguments
    	process(&selArgs)				//run the CLI command
    }
    
  • 接下俩分为initArgs(&selpgArgs)、 handleArgs(len(os.Args), &selpgArgs)、process(&selArgs)三部分

    • initArgs(&selpgArgs) 初始化flag变量,并解析

      func initArgs(args *selpg_args) {
      	pflag.Usage = func() {
      		fmt.Fprintf(os.Stderr,
      			"USAGE: \n%s -s start_page -e end_page [ -f | -l lines_per_page ]" + 
      			" [ -d dest ] [ in_filename ]\n", )
      		pflag.PrintDefaults()
      	}
      	pflag.IntVarP(&args.spage,"start", "s", 0, "start page")
      	pflag.IntVarP(&args.epage,"end","e", 0, "emd page")
      	//这里跟官方文档不同,使用10行作为默认值
      	pflag.IntVarP(&args.plen,"linenum", "l", 10, "page length (lines)")
      	pflag.BoolVarP(&args.ptype,"printdes","f", false, "'l' for lines-delimited, 'f' for form-feed-delimited. default is 'l'")
      	pflag.StringVarP(&args.printdest, "othertype","d", "", "print destination")
      	pflag.Parse() //解析
      }
      
    • handleArgs(argNum int, args *selpg_args) 处理参数

      func handleArgs(argNum int, args *selpg_args) {
      	/* 检查参数合不合法 */
      	if argNum < 3 {
      		fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", programName)
      		pflag.Usage()
      		os.Exit(1)
      	}
      
      	/* 第一个参数,spage*/
      	if os.Args[1][0] != '-' || os.Args[1][1] != 's' {
      		fmt.Fprintf(os.Stderr, "%s: 1st arg should be -s=spage\n", programName)
      		pflag.Usage()
      		os.Exit(2)
      	}
      	if args.spage < 1  {
      		fmt.Fprintf(os.Stderr, "%s: invalid start page %s\n", programName, args.spage)
      		pflag.Usage()
      		os.Exit(3)
      	}
      
      	/* 第二个参数,epage*/
      	if os.Args[3][0] != '-' || os.Args[3][1] != 'e' {
      		fmt.Fprintf(os.Stderr, "%s: 2nd arg should be -e=epage\n", programName)
      		pflag.Usage()
      		os.Exit(4)
      	}
      	if args.epage < 1  || args.epage < args.spage  {
      		fmt.Fprintf(os.Stderr, "%s: invalid end page %s\n", programName, args.epage)
      		pflag.Usage()
      		os.Exit(5)
      	}
          
      	/* 处理可选择的参数 */
      	if args.plen != 5 {
      		if args.plen < 1  {
      			fmt.Fprintf(os.Stderr, "%s: invalid page length %s\n", programName, args.plen)
      			pflag.Usage()
      			os.Exit(6)
      		}
      	}
      
      
      	/* 第三个参数,infile*/
      	if pflag.NArg() > 0 {
      		args.infile = pflag.Arg(0)
      		/*检查文件是否存在 */
      		file, err := os.Open(args.infile)
      		if err != nil {
      			fmt.Fprintf(os.Stderr, "%s: input file \"%s\" does not exist\n", programName, args.infile)
      			os.Exit(7)
      		}
      		/* 是否可读 */
      		file, err = os.OpenFile(args.infile, os.O_RDONLY, 0666)
      		if err != nil {
      			if os.IsPermission(err) {
      				fmt.Fprintf(os.Stderr, "%s: input file \"%s\" exists but cannot be read\n", programName, args.infile)
      				os.Exit(8)
      			}
      		}
      		file.Close()
      	}
      }
      
      
    • handle(args *selpg_args) 处理错误信息

      func handle(args *selpg_args) {
      	//错误处理
      	if len(os.Args) < 3 {	/* Not enough args, minimum command is "selpg -sstartpage -eend_page"  */
      		fmt.Fprintf(os.Stderr, "\n%s: not enough arguments\n", programName)
      		pflag.Usage()
      		os.Exit(1)
      	}
      
      	/* handel -s */
      	if os.Args[1] != "-s" {
      		fmt.Fprintf(os.Stderr, "\n%s: 1st arg should be -s start_page\n", programName)
      		pflag.Usage()
      		os.Exit(2)
      	}
      	i := 1 << 32 - 1
      	if(args.spage < 1 || args.spage > i) {
      		fmt.Fprintf(os.Stderr, "\n%s: invalid start page %s\n", programName, os.Args[2])
      		pflag.Usage()
      		os.Exit(3)
      	}
      
      	/* handle -e*/
      	if os.Args[3] != "-e" {
      		fmt.Fprintf(os.Stderr, "\n%s: 2nd arg should be -e end_page\n", programName)
      		pflag.Usage()
      		os.Exit(4)
      	}
      	if args.epage < 1 || args.epage > i || args.epage < args.epage {
      		fmt.Fprintf(os.Stderr, "\n%s: invalid end page %i\n", programName, args.epage)
      		pflag.Usage()
      		os.Exit(5)
      	}
      	//
      	if len(pflag.Args()) == 1 {
      		_, err := os.Stat(pflag.Args()[0])
      		/* check if file exists */
      		if err != nil && os.IsNotExist(err) {
      			fmt.Fprintf(os.Stderr, "\n%s: input file \"%s\" does not exist\n",
      					programName, pflag.Args()[0]);
      			os.Exit(6);
      		}
      		args.infile = pflag.Args()[0]
      	}
      }
      
    • process(args *selpg_args) 运行结果

      func process(args *selpg_args) {
      	fin := os.Stdin
      	fout := os.Stdout
      	var (
      		 pageCnt int
      		 lineCnt int
      		 err error
      		 err1 error
      		 err2 error
      		 line string
      		 cmd *exec.Cmd
      		 stdin io.WriteCloser
      	)
      	/* 处理fin输入 */
      	if args.infile != "" {
      		fin, err1 = os.Open(args.infile)
      		if err1 != nil {
      			fmt.Fprintf(os.Stderr, "%s: could not open input file \"%s\"\n", programName, args.infile)
      			os.Exit(11)
      		}
      	}
      
      	if args.printdest != "" {
      		//使用exec库的Command的函数调用,command返回cmd结构来执行带有相关参数的命令,它仅仅设定cmd结构中的Path和Args参数
      		cmd = exec.Command("cat", "-n")
      		//指定一个对象连接到对应的管道进行传输参数(stdinpipe),获取输出(stdoutpipe),获取错误(stderrpipe)
      		stdin, err = cmd.StdinPipe()
      		if err != nil {
      			fmt.Println(err)
      		}
      	} else {
      		stdin = nil
      	}
      
      /* 读取文件 */
      //NewReader 相当于 NewReaderSize(rd, 4096)
      	rd := bufio.NewReader(fin)
      	if args.ptype == false {
      		lineCnt = 0		//记录读了多少行
      		pageCnt = 1		//记录读了多少页
      		for true {
      			//用readString的方式读取,直到遇到换行符\n才停止读取
      			line, err2 = rd.ReadString('\n')
      			if err2 != nil { /* error or EOF */
      				break
      			}
      			lineCnt++
      			if lineCnt > args.plen { 	//到达设定的每页最大行数
      				pageCnt++
      				lineCnt = 1
      			}
      			if pageCnt >= args.spage && pageCnt <= args.epage {
      				fmt.Fprintf(fout, "%s", line)
      			}
      		}
      	} else {
      		pageCnt = 1
      		for true {
      			//用ReadByte的方式读取
      			c, err3 := rd.ReadByte()
      			if err3 != nil { /* error or EOF */
      				break
      			}
      			if c == '\f' { //以换页符为标志记录读过的页数
      				pageCnt++
      			}
      			if pageCnt >= args.spage && pageCnt <= args.epage {
      				fmt.Fprintf(fout, "%c", c)
      			}
      		}
      		fmt.Print("\n")
      	}
      
      	/* 循环后的处理*/
      	// pageCnt比开始页面还小,说明出错了
      	if pageCnt < args.spage {
      		fmt.Fprintf(os.Stderr, "%s: spage (%d) greater than total pages (%d), no output written\n", programName, args.spage, pageCnt)
      	} else if pageCnt < args.epage { //pageCnt比结束页面小,同样也是出错
      		fmt.Fprintf(os.Stderr, "%s: epage (%d) greater than total pages (%d), less output than expected\n", programName, args.epage, pageCnt)
      	}
      	
      	//输出文件名不为空
      	if args.printdest != "" {
      		stdin.Close()
      		cmd.Stdout = fout
      		cmd.Run()
      	}
      	fmt.Fprintf(os.Stderr,"\n---------------\nProcess end\n")
      	fin.Close()
      	fout.Close()
      }
      

测试结果

测试文件名称为 in

内容为999行数字(1-999), 每一行的数字表示行数

  • selpg -s1 -e1 input_file
    在这里插入图片描述

  • selpg -s1 -e1 < input_file
    在这里插入图片描述

  • other_command | selpg -s10 -e20

在这里插入图片描述
这个是因为文件没有那么多页造成的,实际上文件只有一页(没有换页符\f), 换一下参数就行了
在这里插入图片描述

  • selpg -s1 -e2 input_file >output_file
    在这里插入图片描述

    控制台并没有输出,输出的信息保存在out文件中

  • selpg -s1 -e2 input_file 2>error_file

在这里插入图片描述

这是因为我的参数后面直接加数字没有隔开空格造成的erro,可能是因为我是ubuntu16.04的原因。

在这里插入图片描述
这样就正常了

  • selpg -s10 -e20 input_file >output_file 2>error_file

在这里插入图片描述

将正确的信息输出到out文件,错误信息输出到error文件

  • selpg -s10 -e20 input_file >output_file 2>/dev/null

在这里插入图片描述
标准输出被重定向至“out”;selpg 写至标准错误的所有内容都被重定向至 /dev/null(空设备),这意味着错误消息被丢弃了。

  • selpg -s10 -e20 input_file >/dev/null

    在这里插入图片描述

    标准输出被丢弃;错误消息在屏幕出现。

  • 其他的测试就不多做介绍了


总结

本次实验要求去阅读源码并将其用Go语言形式表达出来,开发一个简单的selpg命令行指令。其中有一些东西没有提示需要自己去查,比如怎么直接在终端使用selpg指令而不是在IDE里运行,需要设置环境变量。先将selpg.go程序go build,再go install到全局变量文件夹(也可以手动复制到全局变量的文件夹里),之后就可以像我上面那样测试了。

本次实验使我学习了更多的包和函数知识,锻炼了go语言的编程能力。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
任务2:远程桌面 实验时间、地点:1-1-5/6,NA-112 完成情况:通过远程桌面连接成功登录到远程主机,可以进行远程操作。 关联知识点:远程桌面协议、网络配置 对实验的理解:通过远程桌面协议,实现远程连接到另一台主机进行操作,需要正确配置网络参数和远程主机的接收设置。 任务3:1P的计算 实验时间、地点:1-1-5/6,NA-112 完成情况:能够成功计算1P的数值,并验证计算结果。 关联知识点:计算机体系结构、CPU计算原理 对实验的理解:了解计算机中1P的含义和计算方式,通过编写程序或使用计算器等工具进行计算,并验证计算结果的正确性。 任务4:子网划分及汇聚网络设计 实验时间、地点:1-1-5/6,NA-112 完成情况:能够进行子网划分及汇聚网络设计,并验证网络的可用性和性能。 关联知识点:网络拓扑结构、子网划分、路由器配置 对实验的理解:了解网络拓扑结构、IP地址规划和子网划分的原理和方法,通过配置路由器和交换机等设备,实现网络的汇聚和互通。验证网络的可用性和性能。 任务5: VLAN规划 实验时间、地点:1-1-5/6,NA-112 完成情况:能够进行VLAN规划,并验证网络的可用性和性能。 关联知识点:VLAN原理、交换机配置 对实验的理解:了解VLAN的原理和实现方法,通过配置交换机等设备,实现VLAN的创建和划分,验证网络的可用性和性能。 任务6:初识CLI命令 实验时间、地点:1-1-5/6,NA-112 完成情况:能够使用基本的CLI命令,进行设备的配置和管理。 关联知识点:CLI命令、设备管理 对实验的理解:了解CLI命令的基本语法和使用方法,通过实践操作,进行设备的配置和管理。 任务7:二层通道的建立 实验时间、地点:1-1-5/6,NA-112 完成情况:能够通过二层通道建立VLAN间的互通,实现数据的传输。 关联知识点:二层通道、VLAN互通 对实验的理解:了解二层通道的原理和实现方法,通过配置交换机等设备,建立VLAN间的互通,实现数据的传输。 任务8:子网的互连(路由实验) 实验时间、地点:1-1-5/6,NA-112 完成情况:能够通过路由器将不同子网的设备互连,实现数据的传输。 关联知识点:路由器配置、子网互连 对实验的理解:了解路由器的原理和实现方法,通过配置路由器等设备,将不同子网的设备互连,实现数据的传输。 任务9:远程管理和配置 实验时间、地点:1-1-5/6,NA-112 完成情况:能够通过远程管理工具对设备进行管理和配置。 关联知识点:远程管理、设备配置 对实验的理解:了解远程管理的原理和实现方法,通过远程管理工具对设备进行管理和配置。 任务10:Visio画图 实验时间、地点:1-1-5/6,NA-112 完成情况:能够使用Visio工具进行拓扑和网络图的绘制。 关联知识点:Visio工具、拓扑和网络图 对实验的理解:了解Visio工具的使用方法,通过实践操作,进行拓扑和网络图的绘制。 任务11: Wireshark工具抓取ARP报文 实验时间、地点:1-1-5/6,NA-112 完成情况:能够使用Wireshark工具抓取ARP报文,并进行分析和解读。 关联知识点:Wireshark工具、ARP协议 对实验的理解:了解Wireshark工具的使用方法和ARP协议的原理,通过抓取ARP报文,进行分析和解读。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值