以下为该命令参数形式
-s start_page -e end_page [ -f | -l lines_per_page ] [ -d dest ] [ in_filename ]
各参数意义如下
-s start page 表示打印开始的页数
-e end page 表示打印结束页数
-f 可选,表示文件是否以\f作为分页标志,与-l不能一起使用
-l 可选,表示文件几行作为一页,与-f不能一起使用
-d 可选,目标打印机
in_filename 可选,读入的文件,若不选则从标准流输入
根据老师教程是用os包获取命令行参数然后用flag包解析参数
这里os包只需简单调用os.Arg来获取参数即可,这里我们说一下flag包,flag包一般用来解析命令行参数
flag包主要包含一些方法和一个flagSet,后者只是对前者做了封装,这里我选择了用flagSet
/*函数原型*/
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet
/*例子*/
flagSet = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
这是创建一个flagSet,其中第一个参数是程序名字,第二个是参数错误时的错误处理方法
这里os.Arg[0]是程序名字,然后我选择了错误时就终止程序的类型
接下来是定义几个参数
startPage = flagSet.Uint("s", 0, "tart_page, must be specified and greter than 0")
endPage = flagSet.Uint("e", 0, "end_page, must be specified and greter than 0")
pageLen = flagSet.String("l", "", "page_size, exclusive to -f, must be postive,default is 72")
pageType = flagSet.Bool("f", false, "use \\f to divide pages, exclusive to -l")
以上的几个方法将会解析参数并将对应指针返回,对应的几个参数分别为参数的flag,参数默认值,参数描述
这是函数原型,创建flagSet对象后通过以上可方法定义参数
func Uint(name string, value uint, usage string) *uint
定义了参数后我们可以通过调用Parse方法,flagSet就会帮我们解析命令行内输入的字符串然后赋值给对应指针,如果用户写的参数错误就会按照你选择的错误方式处理
这里我还自定义了一种类型,flag中你可以通过Var等方法来给参数赋值自定义类型,但要实现Set()和String()两个接口用于赋值和转字符串
下面是我代码中的例子
type StringArray []string
func (s *StringArray) Set(value string) error {
*s = append(*s, value)
return nil
}
func (s *StringArray) String() string {
return fmt.Sprint([]string(*s))
}
func (s *StringArray) Size() int {
return len(*s)
}
func (s *StringArray) Top() (string, error) {
if s.Size() > 0 {
var temp = s.String()
temp = strings.TrimSuffix(temp, "[")
temp = strings.TrimSuffix(temp, "]")
return temp, nil
}
return "", fmt.Errorf("empty error")
}
根据官网文档建议
If you like, you can bind the flag to a variable using the Var() functions.
var flagvar int
func init() {
flag.IntVar(&flagvar, “flagname”, 1234, “help message for flagname”)
}
因此我在init函数里面对我的一个参数进行了定义
flagSet.Var(&printDest, "d", "destination")
剩下的就是获得参数后大概判断是不是合法参数,这些想必不是什么难点写写判断就可以
关于流部分,首先要确定接受文件的流从哪输入过来
如果没有文件参数的话就是标准流,有的话就用文件流
var inputStream = os.Stdin
if inputFile != "" {
inputStream, err = os.Open(inputFile)
defer inputStream.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "%s : ", programname)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
这里使用os的open函数来打开文件流
然后关于这个流要输出到哪里,我们需要确定,如果存在目的打印机我们把我们读取的文件通过管道传到lp命令的输入流,这里我用bufio的reader来读文件,然后调用os/exec的StdinPipe()方法来将文件输入,当然如果用户没定义-d参数则将输出流输出到屏幕上即可
关键代码如下
stdin = os.Stdout
if printDest.Size() > 0 && pDest != "" {
printer = exec.Command("lp", "-d", pDest)
//printer = exec.Command("cat")
stdin, printErr = printer.StdinPipe()
if printErr != nil {
fmt.Fprintf(os.Stderr, "%s : ", programname)
fmt.Fprintln(os.Stderr, printErr)
}
if *pageType {
err = writeIntoPrintInByF(myReader, *startPage, *endPage)
} else {
err = writeIntoPrintInByLine(myReader, *startPage, *endPage, pLen)
}
if err != nil {
fmt.Fprintf(os.Stderr, "%s : ", programname)
fmt.Fprintln(os.Stderr, err)
}
stdin.Close()
然后这是将读取文件写入流中,这里我调用了Write方法
stdin.Write([]byte(line))
其中line是我调用reader的readString方法获得的字符串,当然也有其他库函数实现读取字符串
line, err := reader.ReadString('\n')
最后是启动命令,这里如果命令出错我们要杀进程,这里我用timer监听实现
printer.Stdout = os.Stdout
printer.Stderr = os.Stderr
if err = printer.Start(); err != nil {
fmt.Fprintf(os.Stderr, "%s : ", programname)
fmt.Fprintln(os.Stderr, printErr)
}
timer := time.AfterFunc(3*time.Second, func() {
printer.Process.Kill()
})
err = printer.Wait()
timer.Stop()
源码位于我的github:https://github.com/caijh23/Go/tree/master/selpg
代码分为两个文件args.go和slepg.go分别为上述讲的两个部分