服务计算--golang开发简单CLI程序
服务计算–golang开发简单CLI程序
这次作业内容看似复杂,老师要求我们用go开发一个简单的CLI程序selpg.go。这是一个命令行界面的应用程序,目的是支持输入参数从而按给定的参数实现快速打印。实际上,selpg.c的源码已经有了,我们需要的只是在此基础上把c语言改成go语言即可。目的是考察我们对go的掌握情况。
话不多说,先上源码。selpg项目
一、CLI是什么
CLI(Command Line Interface)是用户在命令行下交互的程序,由于通过将程序编译到一个静态文件中来减少依赖。在此处,我们的CLI要实现的功能是在命令行下通过输入的参数实现快速打印。
二、selpg.c源码解析
首先,作者定义个一个名为selpg_args的sruct用来存放处理过后的输入信息。
struct selpg_args
{
int start_page;
int end_page;
char in_filename[BUFSIZ];
int page_len; /* default value, can be overriden by "-l number" on command line */
int page_type; /* 'l' for lines-delimited, 'f' for form-feed-delimited */
/* default is 'l' */
char print_dest[BUFSIZ];
};
之后,是main函数。要做的仅仅是创建一个selpg_args的结构体,然后初始化结构体。接下来从命令行读数据,存入struct。最后处理这个额struct即可.
int main(int ac, char **av)
{
sp_args sa;
/* save name by which program is invoked, for error messages */
progname = av[0];
sa.start_page = sa.end_page = -1;
sa.in_filename[0] = '\0';
sa.page_len = 72;
sa.page_type = 'l';
sa.print_dest[0] = '\0';
process_args(ac, av, &sa);
process_input(sa);
return 0;
}c
用来从命令行读取参数,并将这些参数转化为srtuct结构体的数据的函数。
void process_args(int ac, char **av, sp_args* psa)
用来处理输入后srtuct的函数。(文件操作)
void process_input(sp_args sa)
如果编译之后,我们就可以在命令行中运行。
selpg -s startPage -e endPage [-l linePerPage | -f ][-d dest] filename
-s表示开始打印的页码,-e表示结束打印的页码,这两个必须写上;而-l表示按固定行数打印文件,-f表示按照换页符来打印,默认按行;-d则是打印的目的地,默认为屏幕。
三、将selpg.c改写为golang
首先介绍一个flag包,flag可以自动帮我们解析绑定在它身上的给定参数,以及为我们生成合适的help信息。这样我们就可以简化处理读入的参数了。
Golang之使用Flag和Pflag
可以使用民间大神的pflag,更友好。
之后就是改写代码了。
首先导入引用包:
import (
"io"
"os/exec"
"bufio"
"os"
"fmt"
flag "github.com/spf13/pflag"
)
struct改写:
type selpg_args struct {
start_page int
end_page int
in_filename string
page_len int /* default value, can be overriden by "-l number" on command line */
page_type string /* 'l' for lines-delimited, 'f' for form-feed-delimited */
/* default is 'l' */
print_dest string
}
process_args的改写,运用flag包:
func process_args(sa * sp_args) {
flag.IntVarP(&sa.start_page,"start", "s", -1, "start page(>1)")
flag.IntVarP(&sa.end_page,"end", "e", -1, "end page(>=start_page)")
flag.IntVarP(&sa.page_len,"len", "l", 72, "page len")
flag.StringVarP(&sa.page_type,"type", "f", "l", "'l' for lines-delimited, 'f' for form-feed-delimited. default is 'l'")
flag.Lookup("type").NoOptDefVal = "f"
flag.StringVarP(&sa.print_dest,"dest", "d", "", "print dest")
process_input的改写,只是简单的变换下语言。
func process_input(sa sp_args) {
var fin *os.File /* input stream */
/* set the input source */
if len(sa.in_filename) == 0 {
fin = os.Stdin
} else {
var err error
fin, err = os.Open(sa.in_filename)
if err != nil {
fmt.Fprintf(os.Stderr, "\n%s: could not open input file \"%s\"\n",
progname, sa.in_filename)
os.Exit(7)
}
defer fin.Close()
}
/* set the output destination */
bufFin := bufio.NewReader(fin)
var fout io.WriteCloser
cmd := &exec.Cmd{}
四、测试
为了方便测试,我把每页最大行数改为5
1.selpg -s 1 -e 1 input.txt / selpg -s 1 -e 3 input.txt
从第一页打到第三页 / 第一页打到第5页
2.selpg -s 1 -e 1 -l 2 input.txt
从第1页打到第1页第2行
3.selpg -s 1 -e 3 < input.txt
从第1页打到第3页,跟第一个命令一样。
4.selpg -s 1 -e 3 input.txt > output.txt
第1页到第3页打印到output.txt。
5.selpg -s 1 -e 10 input.txt 2> error.txt
错误信息打到error,由于我这里只有1-16行,所以第10页超出(只有4页)。
6.selpg -s 1 -e 10 input.txt > /dev/null
不想把输出打到屏幕,只想在屏幕打出错误信息。