selpg
golang for selpg(CLI)
开发 Linux 命令行实用程序
1. usage
1. $ selpg -s=1 -e=1 input_file
该命令将把“input_file”的第 1 页写至标准输出(也就是屏幕),因为这里没有重定向或管道。
2. $ selpg -s=1 -e=1 < input_file
该命令与示例 1 所做的工作相同,但在本例中,selpg 读取标准输入,而标准输入已被 shell/内核重定向为来自“input_file”而不是显式命名的文件名参数。输入的第 1 页被写至屏幕。
3. $ other_command | selpg -s=10 -e=20
“other_command”的标准输出被 shell/内核重定向至 selpg 的标准输入。将第 10 页到第 20 页写至 selpg 的标准输出(屏幕)。
4. $ selpg -s=10 -e=20 input_file >output_file
selpg 将第 10 页到第 20 页写至标准输出;标准输出被 shell/内核重定向至“output_file”。
5. $ selpg -s=10 -e=20 input_file 2>error_file
selpg 将第 10 页到第 20 页写至标准输出(屏幕);所有的错误消息被 shell/内核重定向至“error_file”。请注意:在“2”和“>”之间不能有空格;这是 shell 语法的一部分(请参阅“man bash”或“man sh”)。
6. $ selpg -s=10 -e=20 input_file >output_file 2>error_file
selpg 将第 10 页到第 20 页写至标准输出,标准输出被重定向至“output_file”;selpg 写至标准错误的所有内容都被重定向至“error_file”。当“input_file”很大时可使用这种调用;您不会想坐在那里等着 selpg 完成工作,并且您希望对输出和错误都进行保存。
7. $ selpg -s=10 -e=20 input_file >output_file 2>/dev/null
selpg 将第 10 页到第 20 页写至标准输出,标准输出被重定向至“output_file”;selpg 写至标准错误的所有内容都被重定向至 /dev/null(空设备),这意味着错误消息被丢弃了。设备文件 /dev/null 废弃所有写至它的输出,当从该设备文件读取时,会立即返回 EOF。
8. $ selpg -s=10 -e=20 input_file >/dev/null
selpg 将第 10 页到第 20 页写至标准输出,标准输出被丢弃;错误消息在屏幕出现。这可作为测试 selpg 的用途,此时您也许只想(对一些测试情况)检查错误消息,而不想看到正常输出。
9. $ selpg -s=10 -e=20 input_file | other_command
selpg 的标准输出透明地被 shell/内核重定向,成为“other_command”的标准输入,第 10 页到第 20 页被写至该标准输入。“other_command”的示例可以是 lp,它使输出在系统缺省打印机上打 印。“other_command”的示例也可以 wc,它会显示选定范围的页中包含的行数、字数和字数。“other_command”可以是任何其它能从其标准输入读取的命令。错误消息仍在屏幕显示。
10. $ selpg -s=10 -e=20 input_file 2>error_file | other_command
与上面的示例 9 相似,只有一点不同:错误消息被写至“error_file”。
$ selpg -s=10 -e=20 -l=10
定义每一页为10行输出
$ selpg -s=10 -e=20 -f
定义文档为f格式,遇到'\f'为一页的终止符
2. test
./selpg -s=1 -e=1 -l=10 text.txt
./selpg -s=1 -e=1 -l=5 \~/Desktop/hello.cpp >\~/Desktop/text.txt
./selpg2 -s=1 -e=1 -f text.txt
./selpg2 -s=1 -e=1
3. design process
导入所需要的包
import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
)
定义结构提体
type selpgArgs struct {
startPage int // start page of the article
endPage int // end page of the article
inFilename string // name of the file to be read
pageLen int /* number of lines in one page, default value is 72,
can be overriden by "-lNumber" on command line */
pageType rune /* type of the article, 'l' for lines-delimited, 'f' for form-feed-delimited
default is 'l'. */
printDest string // destination of result pages
}
函数接口
4.代码
func usage();
func processArgs(argNums int, args []string, saAddr *selpgArgs);
func processInput(saAddr *selpgArgs);
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
)
type selpgArgs struct {
startPage int // start page of the article
endPage int // end page of the article
inFilename string // name of the file to be read
pageLen int /* number of lines in one page, default value is 72,
can be overriden by "-lNumber" on command line */
pageType rune /* type of the article, 'l' for lines-delimited, 'f' for form-feed-delimited
default is 'l'. */
printDest string // destination of result pages
}
var progname string // name of program, used to display error message
func usage() {
fmt.Fprintf(os.Stderr,
"\nUSAGE: %s -s=start_page(number) -e=end_page(number) [ -f | -l=lines_per_page(number) ] [ -ddest ] [ in_filename ]\n", progname)
}
func processArgs(argNums int, args []string, saAddr *selpgArgs) {
// check if the number of arguments is valid
if argNums < 3 {
fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progname)
usage()
os.Exit(1)
}
// handle 1st arg - start page
tmpStr := []rune(args[1])
if tmpStr[0] != '-' || tmpStr[1] != 's'||tmpStr[2] != '=' {
fmt.Fprintf(os.Stderr, "%s: 1st arg should be -sstart_page\n", progname)
usage()
os.Exit(2)
}
page, err := strconv.Atoi(string(tmpStr[3:]))
if page < 1 || err != nil {
fmt.Fprintf(os.Stderr, "%s: invalid start page %s\n", progname, string(tmpStr[3:]))
usage()
os.Exit(3)
}
saAddr.startPage = page
// handle 2nd arg -end page
tmpStr = []rune(args[2])
if tmpStr[0] != '-' || tmpStr[1] != 'e' || tmpStr[2] != '=' {
fmt.Fprintf(os.Stderr, "%s: 2nd arg should be -eend_page\n", progname)
usage()
os.Exit(4)
}
page, err = strconv.Atoi(string(tmpStr[3:]))
if page < 1 || page < saAddr.startPage || err != nil {
fmt.Fprintf(os.Stderr, "%s: invalid start page %s\n", progname, string(tmpStr[3:]))
usage()
os.Exit(5)
}
saAddr.endPage = page
// handle optional args
argIndex := 3
for argIndex < argNums && []rune(args[argIndex])[0] == '-' {
tmpStr = []rune(args[argIndex])
switch tmpStr[1] {
case 'l':
if tmpStr[2]!='=' {
usage()
os.Exit(13)
}
lineNum, err := strconv.Atoi(string(tmpStr[3:]))
if lineNum < 1 || err != nil {
fmt.Fprintf(os.Stderr, "%s: invalid page length %s\n", progname, string(tmpStr[2:]))
usage()
os.Exit(6)
}
saAddr.pageLen = lineNum
argIndex++
case 'f':
if strings.Compare(string(tmpStr), "-f") != 0 {
fmt.Fprintf(os.Stderr, "%s: option should be \"-f\"\n", progname)
usage()
os.Exit(7)
}
saAddr.pageType = 'f'
argIndex++
case 'd':
if len(tmpStr[2:]) < 1 {
fmt.Fprintf(os.Stderr, "%s: -d option requires a printer destination\n", progname)
usage()
os.Exit(8)
}
saAddr.printDest = string(tmpStr[2:])
argIndex++
default:
fmt.Fprintf(os.Stderr, "%s: unknown option %s\n", progname, string(tmpStr))
usage()
os.Exit(9)
} // end switch
} // end while
// there is one more argument
if argIndex <= argNums-1 {
saAddr.inFilename = args[argIndex]
// check if file exists
f, err := os.Open(saAddr.inFilename)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: input file \"%s\" does not exist\n", progname, saAddr.inFilename)
os.Exit(10)
}
f.Close()
}
}
func processInput(saAddr *selpgArgs) {
// set the input source
fin := os.Stdin
var err error
if saAddr.inFilename != "" {
fin, err = os.Open(saAddr.inFilename)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: could not open input file \"%s\"\n", progname, saAddr.inFilename)
os.Exit(11)
}
}
// set the ouput destination
fout := os.Stdout
var cmd *exec.Cmd
if saAddr.printDest != "" {
tmpStr := fmt.Sprintf("./%s", saAddr.printDest)
cmd = exec.Command("sh", "-c", tmpStr)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: could not open pipe to \"%s\"\n", progname, tmpStr)
os.Exit(12)
}
}
// dealing with the page type
var line string
pageCnt := 1
inputReader := bufio.NewReader(fin)
rst := ""
if saAddr.pageType == 'l' {
lineCnt := 0
for true {
line, err = inputReader.ReadString('\n')
if err != nil { // error or EOF
break
}
lineCnt++
if lineCnt > saAddr.pageLen {
pageCnt++
lineCnt = 1
}
if pageCnt >= saAddr.startPage && pageCnt <= saAddr.endPage {
if saAddr.printDest == "" {
fmt.Fprintf(fout, "%s", line)
} else {
rst += line
}
}
}
} else {
for true {
c, _, erro := inputReader.ReadRune()
if erro != nil { // error or EOF
break
}
if c == '\f' {
pageCnt++
}
if pageCnt >= saAddr.startPage && pageCnt <= saAddr.endPage {
if saAddr.printDest == "" {
fmt.Fprintf(fout, "%c", c)
} else {
rst += string(c)
}
}
}
}
if saAddr.printDest != "" {
cmd.Stdin = strings.NewReader(rst)
cmd.Stdout = os.Stdout
err = cmd.Run()
if err != nil {
fmt.Println("print error!")
}
}
if pageCnt < saAddr.startPage {
fmt.Fprintf(os.Stderr, "%s: start_page (%d) greater than total pages (%d), no output written\n", progname, saAddr.startPage, pageCnt)
} else {
if pageCnt < saAddr.endPage {
fmt.Fprintf(os.Stderr, "%s: end_page (%d) greater than total pages (%d), less output than expected\n", progname, saAddr.endPage, pageCnt)
}
}
fin.Close()
fout.Close()
fmt.Fprintf(os.Stderr, "%s: done\n", progname)
}
func main() {
sa := new(selpgArgs)
progname = os.Args[0] // get the name of the program
// initial selpg's arguments to default values
sa.startPage = -1
sa.endPage = -1
sa.inFilename = ""
sa.pageLen = 20
sa.pageType = 'l'
sa.printDest = ""
processArgs(len(os.Args), os.Args, sa)
processInput(sa)
}