命令行工具 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: