go语言编写命令行程序

selPage命令简介

该命令是以标准的linux命令行程序为参照,采用golang进行编写的。
作用是可以从某个输入文档中截取指定页数的文本到标准输出或者重定向文件。

具体用法

该指令支持-s,-e,-l,-f 标准参数以及之后的可选参数。

  1. -s ,指定截取文本的起始页数,该页也包含在内,写法为-sNum,其中Num为正整数,默认为1;
  2. -e ,指定截取文本的末尾页数,用法和-s类似,默认值为1000;
  3. -l ,指定了每一页的行数,程序将会按照指定的行数来确定每一页的结尾,该指令和-f指令不可同时出现;
  4. -f ,单独使用,指定了程序按照换页符 ‘\f’ 来界定每一页的结尾;
    如果-l和-f均未指定的话,将会采用-l的默认值72来实现;
  5. 必选的标准参数inFile,紧跟在所有标准参数之后,用于指明程序的输入来源,应该为某个文件的路径;
  6. 非必选的重定向指令,linux命令行程序中所有的重定向指令都可以加在该指令之后,从而使该指令更加灵活。

使用示例

go selPage.go -s2 -e5 -l60 ./test.file
go selvage.go -s2 -e6 ./test.file >./test2.file
go selvage.go -s2 -e6 -f ./test.file 1&>2
等等。

使用的组件:pflag

导入方式 在将该地址上的包通过 go get 指令下载到本地后,将这行代码添加到package下面:import flag "github.com/spf13/pflag"

pflag包是go自带包flag的一个插件,作用和flag类似,用于处理命令行指令的解析。
具体使用方法可以前往查看源码以及相关博客。

以下是代码中使用到了该包内容的相关部分

flag.typeP

func wordSepNormalizeFunc(f *flag.FlagSet, name string) flag.NormalizedName {
	from := []string{"-", "_"}
	to := "."
	for _, sep := range from {
		name = strings.Replace(name, sep, to, -1)
	}
	return flag.NormalizedName(name)
}
flag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc)
s := flag.IntP("startPage", "s", 1, "this decides the start page, default 1 ")
e := flag.IntP("endPage", "e", 1000, "this decides the end page, default 1000 ")
l := flag.IntP("pageLines", "l", 72, "this decides the lines one page, default 72 ")
flag.StringP("usef", "f", "", "this decides use the \\f, no argument")

flag.Parse()

这段代码作用是告诉程序该如何解析命令行指令后的标准参数的。
其中wordSepNormalizeFunc函数用于将用户误输的_和-正确解析为.,
之后主要使用对应的typeP类型函数,将参数的名称,简写,默认值以及介绍告诉程序,以便之后的解析。
在完成相关设定之后,需要调用flag.Parse函数来完成提交。

flag.Arg

flag.Args()用于获取所有的非标准参数,也就是不带-的参数。
flag.Arg(num int)用于获取指定位置的非标准参数。
flag.NFlag() / flag.NArg()分别用于给出标准 / 非标准参数的个数。
flag.LookUp(name string).changedbool类型,LookUp函数通过名称检索标准参数,并判断该参数是否被设定。(设定了则changed属性为true)

if *s > *e {
	ErrReport("startPage must smaller than or equal with endPage")
}
isL, isF := flag.Lookup("pageLines").Changed, flag.Lookup("usef").Changed
if isL && isF {
	ErrReport("l and f argus at the same time")
}

这段代码是对用户的输入进行初步检测,比如起始页数必须不大于结尾页数,-l和-f不能同时存在等等。
程序中出现的用户类错误均交由ErrReport函数处理。

func ErrReport(err ...string) {
	var strs = err
	for _, str := range strs {
		_, _ = fmt.Fprintln(os.Stderr, str)
	}
	os.Exit(2)
}

重定向处理

在这一部分,程序直接使用多个if-else对flag.Args获得的非标准参数进行循环处理。
着重处理包含>fileName, 1/2>&fileName, 1/2&>1/2三种情况。
而go语言为我们提供了一定的重定向机制,比如:os.stdout = os.stderr 将会将标准输出内容重定向至标准错误输出。

var outFile string

for n, arg := range flag.Args() {
	if n == 0 {
		continue
	}
	if arg[0:1] == ">" {
		outFile = arg[1:]
		f, err := os.OpenFile(outFile, os.O_RDWR | os.O_APPEND, 666)
		if err != nil {
			panic(err)
		}
		os.Stdout = f
	}else if arg[1:3] == "&>" {
		outFile = arg[3:]
		f, err := os.OpenFile(outFile, os.O_RDWR | os.O_APPEND, 666)
		if err != nil {
			panic(err)
		}
		if arg[0:1] == "1" {
			os.Stdout = f
		}else {
			os.Stderr = f
		}
	}else if arg[1:3] == ">&" {
		if arg[0:1] == "1" {
			os.Stdout = os.Stderr
		}else {
			os.Stderr = os.Stdout
		}
	}else {
		ErrReport("无法解析该命令:", arg)
	}
}

由于需要打开文件进行修改,因此这里不能使用默认的os.Open函数,从该函数获取的*File指针将只具有读的权限,因此需要os.OpenFile函数,自行设置文件权限。

文件输入

对应页数的文本获取方法有两种,一是由-l指定的根据行数计算页数,另外则是由-f指定的根据换页符计算页数。但大体上,两种方法的工作机理都是外层循环统计当前页数,内部逻辑处理页尾的判定。
首先将文件开头非目标文本段通过readBytes('\n')或者readBytes('\f')抛弃,
在到达指定开头后,再次通过该函数将目标文本读取到一个外部的string类型数据中保存。

func readByLine(f *os.File, s, e, l int) {
	countL, countP := 0, 0
	reader := bufio.NewReader(f)
	for ;countP < s-1; {
		countL = 0
		for {
			_, _ = reader.ReadBytes('\n')
			countL++;
			if countL >= l {
				countP++
				break
			}
		}
	}
	for ; countP < e; {
		countL = 0;
		for {
			bytes, err := reader.ReadBytes('\n')
			if err != nil {
				panic(err)
			}
			lines += string(bytes)
			_, _ = fmt.Fprintf(os.Stderr, "读入 %d 个字节\n", len(bytes))
			countL++;
			if countL >= l {
				countP++
				break
			}
		}
	}
}
func readByF(f *os.File, s, e int) {
	countP := 0
	reader := bufio.NewReader(f)
	for ;countP < s-1; {
		_, _ = reader.ReadBytes('\f')
	}
	for ; countP < e; {
		bytes, err := reader.ReadBytes('\f')
		if err != nil {
			panic(err)
		}
		lines += string(bytes)
		_, _ = fmt.Fprintf(os.stderr, "读入 %d 个字节", len(bytes))
	}
}

至于内容输出,则是简单地将保存的string类型数据放置到标准输出中,因为前面已经进行了输出的重定向,因此这里也就会被输出到相应的地方去。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值