文章目录

  • selpg介绍
  • 命令行准则
    • 输入
    • 输出
    • 错误输出
    • 执行
    • 命令行参数
  • selpg参数介绍
  • Golang实现
    • 数据结构
    • 参数解析
    • 校验参数
    • 处理命令输入
      • readFile函数
    • 程序测试

selpg介绍

本文介绍开发selpg(SELect PaGes),一个类似于catlspr等标准命令的Linux命令行实用程序。selpg允许用户指定从输入文本抽取的页的范围,这些输入文本可以来自文件或者另一个进程,然后将输入进行打印操作。由于运行环境为CentOS7系统的虚拟机,并且并没有实际的打印机,所以我们需要在虚拟机中安装虚拟打印机。具体方法参见传送门。

命令行准则

在实现selpg的过程中,我们需要掌握命令行的标准,从而可以开发出更加对用户更加有好的,确保用户以更加灵活的方式使用该程序。

主要准则包括输入、输出、错误输出、执行以及命令行参数。简要介绍:

输入

$ command input_file     # 从input_file获取标准输入(stdin)
$ command                   # 从终端获取标准输入
$ command < input_file       # 利用 < 重定向标准输入,从input_file获取输入
$ other_command | command   # 利用 | (pipe)使得标准输入来自另一个程序的标准输出

输出

$ command                    # 标准输出(stdout)输出到终端
$ command > output_file      # 利用 > 重定向标准输出到output_file
$ command | other_command   # 利用 | (pipe)将标准输出成为另一命令的标准输入

错误输出

$ command                            # 标准错误(stderr)被输出到终端
$ command 2>error_file               # 标准错误重定向到error_file
$ command >output_file 2>error_file   # 标准输出和标准错误重都定向到文件
$ command 2>&1                       # 将标准错误发送至标准输出被重定向的任何位置,此命令中标准错输出为终端,所以标准错误也为终端
$ command >output_file 2>&1           # 将标准错误和标准输出都被定向到output_file

执行

不管程序的输入源(文件、管道或终端)和输出目的地是什么,程序都应该以同样的方式工作。这使得在如何使用它方面有最大的灵活性。

命令行参数

作为选项的命令行参数右前缀“-”或者“–”标识;另一类参数是那些不是选项的参数,并不改变程序的行为,这类参数代表要处理的文件名等等。

Linux 实用程序语法图看起来如下:

$ command mandatory_opts [ optional_opts ] [ other_args ]

其中:

  • command 是命令本身的名称。
  • mandatory_opts 是为使命令正常工作必须出现的选项列表。
  • optional_opts 是可指定也可不指定的选项列表,这由用户来选择;但是,其中一些参数可能是互斥的。
  • other_args 是命令要处理的其它参数的列表;这可以是任何东西,而不仅仅是文件名。

以上内容整理来自开发Linux命令行实用程序

selpg参数介绍

参数(值) 是否必须 介绍
s(start_page_num) 必须 表示从输入数据的第start_page_num开始
e(end_page_num) 必须 表示从输入数据的第end_page_num结束
l(page_len) 非必须 表示输入数据每page_len行为1页
f 非必须 表示输入数据以\f为换页符
d(Destination) 非必须 Destination是lp命令的“-d”选项可接受的打印目的地名称;即selpg命令加上-d参数之后,表示Destination打印机打印输入数据。若要验证该选项是否已经生效,可以运行命令“lpstat -t”,查看添加到“Destination”打印队列是否有此打印作业。如果当前打印机可用,则会打印该输出,这一特性是用popen()系统调用来实现的,该系统调用允许一个进程打开到另一个进程的管道,将管道用于输出或输入。

Golang实现

数据结构

type Selpg_Args struct {start_page *int          // -s参数值(开始页面数)end_page   *int            // -e参数值(结束页面数量)page_len    *int      // -l参数值(以page_len行为1页)page_type_f *bool      // -f参数值(以\f为分页符) output      *string     // -o参数值(输出文件名)(可忽略)input       string      // 输入文件名dest_print  *string     // -d参数值(打印目的地)
}

参数解析

利用pflag第三方库可以方便处理参数情况,有关flag和pflag库介绍详见传送门

func initial() {// selpg_args.page_len = 72// 添加shorthand参数(去掉方法后面的字母P即取消shorthand参数)selpg_args.start_page = pflag.IntP("start_page", "s", -1, "start page")selpg_args.end_page = pflag.IntP("end_page", "e", -1, "end page")selpg_args.page_type_f = pflag.BoolP("form_feed", "f", false, "delimited by form feeds")selpg_args.page_len = pflag.IntP("limit", "l", 72, "delimited by fixed page length")selpg_args.output = pflag.StringP("output", "o", "", "output filename")selpg_args.dest_print = pflag.StringP("dest", "d", "", "target printer")// 另外一种写法// pflag.IntVarP(selpg_args.start_page, "start_page", "s", -1, "start page")// pflag.IntVarP(selpg_args.end_page, "end_page", "e", -1, "end page")// pflag.BoolVarP(selpg_args.page_type_f, "form_feed", "f", false, "delimited by form feeds")// pflag.IntVarP(selpg_args.page_len, "limit", "l", 72, "delimited by fixed page length")// pflag.StringVarP(selpg_args.output, "output", "o", "", "output filename")// pflag.StringVarP(selpg_args.dest_print, "dest", "d", "", "target printer")selpg_args.input = ""pflag.Usage = usagepflag.Parse()
}// usage 函数定义如下:
func usage() {fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])fmt.Fprintf(os.Stderr, os.Args[0]+" -sstart_page_num -eend_page_num [ -f | -llines_per_page ] [ -doutput ] [ input ]\n")fmt.Fprintln(os.Stderr, "[OPTIONS]:")fmt.Fprintln(os.Stderr, "   filename string       input filename")pflag.PrintDefaults() // PrintDefaults()表示输出默认的提示信息
}

校验参数

func handle_args() {/* if len(os.Args) < 3, then we can know that -s or -e is not entered. *//* if *selpg_args_.start_page == -1, then arg -s isn't entered, because -s-1 is not allowed(pflag can't analysis). */if len(os.Args) < 3 || *selpg_args.start_page == -1 || *selpg_args.end_page == -1 {fmt.Println("You are expected to input the start_page_num and end_page_num which will be greater than zero")pflag.Usage()os.Exit(1)}/* judge the start_page_num and end_page_num *//* INT_MAX definition: const INT_MAX = int(^uint(0) >> 1) */if *selpg_args.start_page <= 0 {checkErr(errors.New("The start_page_num can't be less than or equal to zero"))}if *selpg_args.start_page > INT_MAX {checkErr(errors.New("The start_page_num can't be greater than INT_MAX: " + strconv.Itoa(INT_MAX)))}if *selpg_args.end_page <= 0 {checkErr(errors.New("The end_page_num can't be less than or equal to zero"))}if *selpg_args.end_page > INT_MAX {checkErr(errors.New("The end_page_num can't be greater than INT_MAX: " + strconv.Itoa(INT_MAX)))}if *selpg_args.start_page > *selpg_args.end_page {checkErr(errors.New("The end_page_num can't be greater than start_page_num"))}/* judge the page_len */if *selpg_args.page_len <= 0 {checkErr(errors.New("The -l limit can't be less than or equal to zero"))}for _, arg := range os.Args[1:] {/* in the following judgements we can also use method: strings.HasPrefix() *//* judge the option -f */if matched, _ := regexp.MatchString(`^-f`, arg); matched {if len(arg) > 2 {checkErr(errors.New(os.Args[0] + ": option should be only \"-f\""))}}/* judge the dest printer */if matched, _ := regexp.MatchString(`^-d`, arg); *selpg_args.dest_print == "" && matched {checkErr(errors.New(os.Args[0] + ": option -d requires a printer destination\n"))}/* judge the output file */if matched, _ := regexp.MatchString(`^-o`, arg); *selpg_args.output == "" && matched {checkErr(errors.New(os.Args[0] + ": option -o requires a output filename\n"))}/* store the input filename */if arg[0] != '-' {selpg_args.input = argbreak}}
}

处理命令输入

func handle_input() {/* define InputFile = os.Stdin or *os.File */var InputFile *os.Fileif selpg_args.input != "" {var err errorInputFile, err = os.Open(selpg_args.input)if err != nil {fmt.Fprintf(os.Stderr, "An error occurred on opening the inputfile: %s\nDoes the file exist?\n", selpg_args.input)os.Exit(1)}} else {InputFile = os.Stdin}defer InputFile.Close()  // close the file before the end of the functionvar OutputFile *os.Filevar cmd *exec.Cmdif *selpg_args.output != "" {var err errorOutputFile, err = os.Create(*selpg_args.output)checkErr(err)} else if *selpg_args.dest_print != "" {fmt.Printf("Printer: %s\n", *selpg_args.dest_print)cmd = exec.Command("lp", "-d", *selpg_args.dest_print)/* the command below can also work */// cmd = exec.Command("lpr", "-P", *selpg_args.dest_print) } else {OutputFile = os.Stdout}defer OutputFile.Close()/* use io.Pipe to create the pipe between input(including reading from file and stdin) and output(including writing to file, stdout and printing the data) */r, w := io.Pipe()/* create the go routine to read from stdin or file and put the data to PipeWriter*/go readFile(w, InputFile)/* define buffer to read from the PipeReader */buf := new(bytes.Buffer)buf.ReadFrom(r)/* cmd != nil denotes that *selpg_args.dest_print != "" and we need to print the input data */if cmd != nil {/* define the reader and assign it to cmd.Stdin which denotes the input of the command(lp -ddest_print) */cmd.Stdin = strings.NewReader(buf.String())var outErr bytes.Buffercmd.Stderr = &outErr/* execute the command(lp -ddest_print) */err := cmd.Run()if err != nil {checkErr(errors.New(fmt.Sprint(err) + ": " + outErr.String()))}}/* if we use argument -o, then we can write the input data to a file */if OutputFile != nil {OutputFile.WriteString(buf.String())}
}

readFile函数

func readFile(w *io.PipeWriter, InputFile *os.File) {/* define the reader */inputReader := bufio.NewReader(InputFile)/* default delimit = '\n' */delimit := '\n'pages := 0lines := 0inputString := ""if *selpg_args.page_type_f {delimit = '\f'}for {/* we use the delimit as the delimiter to read from the file */inputSubString, readerError := inputReader.ReadString(byte(delimit))/* handle the input, replace all form feeds '\f' by '', then our print will not be influenced */inputSubString = strings.Replace(inputSubString, "\f", "", -1)/* inputString will store one page's content */inputString += inputSubString/* when delimiter is '\n', then we need to count the lines until lines are equal to *selpg_args.page_len, then we can print the page. */if delimit == '\n' {lines++if lines == *selpg_args.page_len {pages++lines = 0/* if pages is within the expected scope, we write it to PipeWriter. */if pages >= *selpg_args.start_page && pages < *selpg_args.end_page {print_pages++fmt.Fprint(w, inputString+"\f")inputString = ""}}} else { /* the delimiter is '\f' */pages++if pages >= *selpg_args.start_page && pages < *selpg_args.end_page {print_pages++fmt.Fprint(w, inputString+"\f")inputString = ""}}/* the final page */if pages >= *selpg_args.end_page {print_pages++fmt.Fprint(w, inputString)break}/* the end of the file */if readerError == io.EOF {if inputString != "" && inputString != "\n" && inputString != "\f" {print_pages++fmt.Fprint(w, inputString)}break}}/* close the PipeWriter */ w.Close()
}

程序测试

输入文件input.txt如下:(按照换行符一共14行,按照换页符一共7页)

测试文件输入,控制台输出

$ ./selpg -s1 -e1 input_file
$ ./selpg -s1 -e1 < input_file

$ other_command | selpg -s1 -e1

$ ./selpg -s1 -e2 input_file >output_file 2>error_file

将第1页第二页写到标准输出,所有的错误信息被shell /内核重定向到“error_file”,2和>之间不能有空格。


$ ./selpg -s1 -e2 input_file >output_file 2>/dev/null

selpg 将第 1 页到第 2 页写至标准输出,标准输出被重定向至“output_file”;selpg 写至标准错误的所有内容都被重定向至 /dev/null(空设备),这意味着错误消息被丢弃了。设备文件 /dev/null 废弃所有写至它的输出,当从该设备文件读取时,会立即返回 EOF。

$ ./selpg -s1 -e2 input_file >/dev/null

selpg 将第 10 页到第 20 页写至标准输出,标准输出被丢弃;错误消息在屏幕出现。

$ ./selpg -s1 -e2 input_file 2>error_file | other_command

$ ./selpg -s1 -e2 -l2 input_file

$ ./selpg -s1 -e2 -f input_file

由输入文件,前三行为第一页,接下来两行为第二页

$ ./selpg -s1 -e2 -dCups-PDF input_file




$ ./selpg -s1 -e2 input_file > output_file 2>error_file &

该命令利用了 Linux 的一个强大特性,即:在“后台”运行进程的能力。在这个例子中发生的情况是:“进程标识”(pid)如 1234 将被显示,然后 shell 提示符几乎立刻会出现,使得您能向 shell 输入更多命令。同时,selpg 进程在后台运行,并且标准输出和标准错误都被重定向至文件。这样做的好处是您可以在 selpg 运行时继续做其它工作。


可以使用ps命令来查看该进程的状态,由于瞬间完成,所以并没有看到该进程。
至此,selpg命令已经基本实现。

服务计算 | Selpg Golang实现相关推荐

  1. 服务计算 - 3 Golang开发Linux命令行实用程序 - selpg

    文章目录 Golang开发Linux命令行实用程序 - selpg 1. 介绍 2. 设计与实现 2.1 设计思路 2.2 功能模块划分与实现 3. 参考文献 Golang开发Linux命令行实用程序 ...

  2. 服务计算作业三——CLI 命令行实用程序开发基础

    服务计算作业三--CLI 命令行实用程序开发基础 18342138 郑卓民 本次作业gitee仓库链接(完整代码) 概述 CLI(Command Line Interface)实用程序是Linux下应 ...

  3. 服务计算作业二——GO语言TDD实践报告

    服务计算作业二--GO语言TDD实践报告 服务计算作业二--GO语言TDD实践报告 教程学习 为一个重复字符五次的函数编写测试,并先使用最少的代码让失败的测试先跑起来(核心概念) 把代码补充完整,使得 ...

  4. 西华大学计算机学院陈鹏,中国计算机学会CCF服务计算专委会走进西华大学

    近日,"中国计算机学会CCFF服务计算专委走进高校活动"来到西华大学.此次活动邀请到CCF服务计算专委会副主任.天津大学冯志勇教授,南京大学马晓星教授,CCF服务计算专委会副秘书长 ...

  5. 使用c++制作微服务计算服务

    其他链接 上次使用了go来制作微服务计算服务 go制作微服务计算服务 c++制作标准差服务计算 使用c++的计算服务速度是更快的,一下只是一个计算标准差的函数样例,和go服务之间进行交换可以使用更简单 ...

  6. 华为公有云服务-计算类(2)

    华为公有云服务-计算类(2) 如何在公有云上购买使用ECS(网络配置)? ECS管理-登录windows系统 ECS ECS管理-登录Linux系统 ECS ECS管理 - 重装/切换操作系统 ECS ...

  7. 无服务计算的未来和挑战: A Berkeley View on Serverless Computing

    加州大学伯克利分校继 2009 年发布 <The Berkeley View on Cloud Computing>一举拨开云计算迷雾,十年后又一次发布了 <A Berkeley V ...

  8. 服务计算 -- 搭建私有云

    服务计算 – 搭建私有云 文章目录 服务计算 -- 搭建私有云 下载VirtualBox及所需镜像 安装虚拟机 配置虚拟机存储位置 创建虚拟机内部虚拟网络 创建Base虚拟机(Centos为例) 链接 ...

  9. 无服务计算应用场景探讨及 FaaS 应用实战

    简介: 无服务计算本身是一个概念或者理论模型,落地到具体技术上主要有函数即服务(FaaS)以及后端即服务(BaaS)两种形式,阿里云提供函数即服务 FaaS 产品. 作者 | 宋文龙(闻可)  阿里云 ...

最新文章

  1. Servlet使用适配器模式进行增删改查案例(jdbc.properties)
  2. php 命名空间通俗易懂_PHP进阶由浅入深掌握面向对象开发
  3. 五大技术助力,智能门锁能否成为智能家居下一个入口?
  4. service获取selinux权限_属性问题展开的selinux权限介绍
  5. linux命令grep和find怎么用,Linux下find和grep常用命令及区别介绍
  6. 【Excel-2010】导入网站数据
  7. 从 JavaScript 到 TypeScript 6 - Vue 引入 TypeScript
  8. MATLAB获取字符串中两个特定字符之间的内容
  9. matlab 画图函数plot
  10. android单选题数据库,数据库系统工程师题库
  11. 新加坡全面开放边境,畅游畅游《摘金奇缘》新加坡地标性景点
  12. 鸟哥的Linux私房菜:首次登陆与在线求助,LINUX最简单的命令及应用(一)
  13. 腾讯金融云mysql,腾讯云金融级云数据库优势与功能介绍
  14. 微信开放平台之第三方平台开发,模板小程序如何提交?
  15. 2023华为OD机试备考攻略 以及题库目录分值说明 考点说明
  16. 无线打印服务器怎么安装,【DDwifi打印服务器】Windows 7系统添加打印机步骤(离线安装打印机驱动)...
  17. SQL Server修改表结构,添加约束
  18. 「GoCN酷Go推荐」go语言位操作库 — bitset
  19. 全志H616芯片香橙派Orange Pi Zero2开发板26pinGPIO口测试
  20. 我爱你宠物网——创业笔记(三)

热门文章

  1. 前端人真实项目中遇到的问题总结
  2. labview can通讯上位机,调用周立功库文件
  3. 河北省报考高级经济师 职称计算机免试条件,河北省高级经济师须职称评定条件...
  4. midjourney入口是什么?怎么使用midjourney
  5. 使用CNN-XGboost模型进行恶意软件分类
  6. 用Magicavoxel像搭积木一样来做一个3D像素场景
  7. VS中的经典字体推荐
  8. 别错过!监理工程师报名流程!
  9. rt3290+linux驱动下载,Ralink RT3290 蓝牙驱动 (雷凌)
  10. 这是一条没用的获取C币的文章