使用 Golang 开发简单的 CLI 程序 selpg

开发实践

使用 golang 开发 开发 Linux 命令行实用程序 中的 selpg

实现过程

文档中讲解得非常仔细,我是完全按照文章的思路,参考原 C 程序的结构,然后用 golang 进行实现。golang 中有用于命令行参数解析、文件读写和输入输出的包,可以很方便地实现 CLI 程序的一些复杂功能。

selpg 的 golang 代码
下面对代码的各部分进行解释,并对程序进行测试,检查是否满足文档中 使用 selpg 章节的要求

代码注释

以注释“==== imports=====”开始的行
导入所需的包。

io,实现了一系列非平台相关的 IO 相关接口和实现,比如提供了对 os 中系统相关的 IO 功能的封装。我们在进行流式读写(比如读写文件)时,通常会用到该包。

os/exec,执行外部命令,它包装了 os.StartProcess 函数以便更容易映射到 stdin 和 stdout,并且利用 pipe 连接 I/O。

bufio,在 io 的基础上提供了缓存功能。在具备了缓存功能后, bufio 可以比较方便地提供 ReadLine 之类的操作。

os,提供了对操作系统功能的非平台相关访问接口。接口为Unix风格。提供的功能包括文件操作、进程管理、信号和用户账号等。

fmt,实现格式化的输入输出操作,其中的 fmt.Printf() 和 fmt.Println() 是开发者使用最为频繁的函数。

flag,提供命令行参数的规则定义和传入参数解析的功能。绝大部分的 CLI 程序都需要用到这个包。(代码中使用 pflag 替代 flag 以满足 Unix 命令行规范, 参考:Golang之使用Flag和Pflag

以注释“==== types =====”开始的行
这里只定义了一个类型,即:selpg_args 结构。指向该类型变量的指针被传递到 process_args() 函数,函数执行后,它包含从参数处理过程获得的值。 type 用来给类型一个短名 sp_args。

以注释“==== globals ======”开始的行
Progname 是保存名称(命令就是通过该名称被调用)的全局变量,作为在错误消息中显示之用。用这种方法,即使您将 selpg 命令重命名为别的名称,新的名称也将在消息中显示;您不必修改该代码。

main() 函数
声明一个 selpg_args 结构变量 sa,用指针 &sa 调用函数 process_args()。process_args() 返回后,已解析的参数值在 sa 结构中;将该变量传递至函数 process_input() ,该函数选择所需的页并将其写至指定的目的地。

如果代码中任何一处出现了使处理不能继续进行之类的错误,那么我们会检索系统错误消息(如果有的话),然后将它与我们自己的消息一起显示。随后我们用错误码调用 exit() 函数;对于本实用程序,我们已经选择了对每个不同错误条件返回不同数字。

process_args() 函数
使用 pflag 绑定 sa中的变量,就能够很简单地将命令行参数解析并保持在 sa 结构中。然后对命令行参数执行错误检查。

以注释“check the command-line arguments”开始的行
我们检查是否传递了最小数目(三)的命令行参数。这三个参数是:

  • 命令名本身
  • -s Number 选项
  • -e Number 选项

如果少于三个参数,则打印消息并退出。请注意对 usage() 函数的调用;这让用户知道调用实用程序的正确方法。这是编写通用实用程序时应该遵守的又一个约定。

以注释“handle 1st arg - start page”开始的行
检查指示起始页选项的参数名是否为“-s”。如果不是,则进行出错退出。
如果没有问题,用 INT_MAX 检查参数值是否为这个平台的有效正整数。

以注释“handle 2nd arg - end page”开始的行
进行的检查和操作与第一个参数基本相同。唯一的额外工作是检查所给的结束页不小于起始页。

以注释“now handle optional args”开始的行
处理余下的参数 page_len 和 in_filename。(page_type 直接由 pflag 解析就可以了)

process_input() 函数
如果命令行上没有给出文件名参数,我们就使用标准输入和标准输出。否则,我们打开指定的文件进行读写。如果文件不能打开,则进行出错退出。然后根据 page_type 判断是由换页符定界还是由行数定界,进行相应的输出。
要点:

  • golang 文件读写、读环境变量,使用 os 包
  • “-dXXX” 实现,请自己查 os/exec 库,例如案例 Command,管理子进程的标准输入和输出通常使用 io.Pipe,具体案例见 Pipe

测试 selpg

测试文件"test.txt"为数字1~30,每个数字占一行

  • selpg,该命令输入参数不符合要求,报错

  • selpg -s 1 -e 1 test.txt,该命令将把“test.txt”的第 1 页(默认10行为一页)写至标准输出(也就是屏幕),因为这里没有重定向或管道。

  • selpg -s 1 -e 1 < test.txt,selpg 读取标准输入,而标准输入已被 shell/内核重定向为来自“test.txt”而不是显式命名的文件名参数。输入的第 1 页被写至屏幕。

  • hello | selpg -s 1 -e 3 -f,hello程序的标准输出(见下图)被 shell/内核重定向至 selpg 的标准输入。将第 1 页到第 3 页(由换页符定界)写至 selpg 的标准输出(屏幕)。
    在这里插入图片描述

  • selpg -s 1 -e 1 -l 5 test.txt >out.txt,selpg 将第 1 页(5行为一页)写至标准输出;标准输出被 shell/内核重定向至“out.txt”。

  • selpg -s 3 -e 5 test.txt 2>error.txt,selpg 将第 3 页到第 5 页写至标准输出(屏幕);所有的错误消息被 shell/内核重定向至“error.txt”。请注意:在“2”和“>”之间不能有空格;这是 shell 语法的一部分

猜你喜欢

转载自blog.csdn.net/For_course/article/details/83028953