开发实践
使用 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 语法的一部分