命令行工具 selpg

selpg 允许用户指定从输入文本抽取的页的范围,这些输入文本可以来自文件或另一个进程。详情可见开发 Linux 命令行实用程序

实现过程

为了解析命令行参数,需要安装 pflag 包:

go get github.com/spf13/pflag

首先使用 pflag 包定义命令行参数:

// flags
startPage := pflag.IntP("start page", "s", 0, "Start page to print")
endPage := pflag.IntP("end page", "e", 0, "End page to print")
pageLen := pflag.IntP("page length", "l", 72, "The number of lines in each page")
pageType := pflag.BoolP("page type", "f", false, "Whether '\\f' is page breaker or not")
printDest := pflag.StringP("print dest", "d", "", "The destination to print")

解析命令行参数并检验它们的合法性:

// parse flags
pflag.Parse()

// check flags
if *startPage < 0 || *endPage < 0 {
    return errors.New("The page number should be positive.")
}
if *startPage > *endPage {
    return errors.New("The start page number should be smaller than the end page number.")
}
if *pageLen != 72 && *pageType == true {
    return errors.New("The \"-l\" flag and the \"-f\" flag shouldn't be used together.")
}
if *pageLen < 0 {
    return errors.New("The page size should be positive.")
}
if pflag.NArg() > 1 {
    return errors.New("Too many arguments.")
}

使用 bufio 包创建读取器:如果命令行中提供了文件名,则在文件中读取;否则在标准输入中读取。

// create reader
var reader *bufio.Reader
if pflag.NArg() == 1 {
    // read from file
    file, err := os.Open(pflag.Arg(0))
    if err != nil {
        return err
    }
    defer file.Close()
    reader = bufio.NewReader(file)
} else {
    // read from stdin
    reader = bufio.NewReader(os.Stdin)
}

如果设定了 -f 标志,则以 -f 为间隔符逐页读取;否则逐行读取,并根据默认的或用户设置的行数来确定每个页的大小。最后根据用户设置的页数范围将所需的内容写入缓冲区。

// write target pages to buffer
var buf strings.Builder
if *pageType == true {
    // read one page each time
    pageCount := 0
    for {
        page, err := reader.ReadBytes('\f')
        if err == io.EOF {
            break
        } else if err != nil {
            return err
        }
        pageCount += 1
        if pageCount >= *startPage && pageCount <= *endPage {
            buf.Write(page)
        } else if pageCount > *endPage {
            break
        }
    }
} else {
    // read one line each time
    pageCount := 0
    lineCount := 0
    for {
        line, err := reader.ReadBytes('\n')
        if err == io.EOF {
            break
        } else if err != nil {
            return err
        }
        lineCount += 1
        if lineCount > *pageLen {
            pageCount += 1
            lineCount = 1
        }
        if pageCount >= *startPage && pageCount <= *endPage {
            buf.Write(line)
        } else if pageCount > *endPage {
            break
        }
    }
}

如果用户没有设置输出位置,则直接将结果输出至标准输出;而如果用户指定了打印机的位置,则调用 lp 命令来将结果输出至目标打印机中。

// write buffer to destination
if *printDest == "" {
    // write to stdout
    fmt.Printf("%s", buf.String())
} else {
    // write to printer
    cmd := exec.Command("lp", "-d" + *printDest)
    cmd.Stdin = strings.NewReader(buf.String())
    var stderr bytes.Buffer
    cmd.Stderr = &stderr
    err := cmd.Run()
    if err != nil {
        return fmt.Errorf("Write to printer failed with %s.\n Message: %s\n", err, stderr.Bytes())
    }
}

最后,使用 Go 工具构建并安装该程序:

go install github.com/whichxjy/Service-Computing-Homework/selpg

测试结果

Updated: