脚本工具的强大

Dec 27 2019

接触过 Linux 系统的应该多少都接触过它强大的全家桶系列中的某些工具,比如 grep, sed, awk, xargs 等

有时候遇到一些问题,我个人的感受是使用脚本工具比使用编程语言更快捷,更方便,更舒服!比如,文本搜索,我通常的选择优先级都是:脚本工具 > Python > Java

一个核心的原因就是,脚本工具是经过大量的人大量的使用测试过的,起码在查询方面的效率和 bug 率,绝对低于自己重新开发一套东西

今天就举个例子,介绍使用工具一行代码搞定的问题。

调试

需要从若干 1GB 的文本文件中,查找某个字段,并且打印输出这个字段在文件中最后一次出现时该行的内容

这种场景我基本都不考虑用 Java 做,打包运行很费劲,Pass

用 Python,实现起来也简单,但需要 IDE 啊,总觉得太吃内存了,不是一个大工程我也不考虑开它

顺理成章想到的就是用 grep 去查找,于是转换为如下的思路:

grep 负责查找,tac 负责从文件尾部开始输出文本,head 负责输出一行内容,于是一行命令如下:

1
2
3
4
λ tac test.asc |grep "2A1             Rx" |head -n 1
3096.986719 1 2A1 Rx d 8 02 00 00 00 00 3F 2E 56 Length = 231910 BitCount = 120 ID = 673
grep: write error: Illegal seek
tac: write error

目的达到了,但 grep 和 tac 报错了,应该是 head 工具在输出一行内容后就关闭了,但是没有关闭 grep 和 tac 的输出流导致,有“洁癖”的我接受不了这种没影响的 error

由于 grep 自己也能控制只输出一行,于是改进命令为

1
2
3
λ tac test.asc |grep -m 1 "2A1             Rx"
3096.986719 1 2A1 Rx d 8 02 00 00 00 00 3F 2E 56 Length = 231910 BitCount = 120 ID = 673
tac: write error

tac 还是有个 error,原因是 grep 也只是关闭了自己。考虑到 tail 也可以实现从尾部读文件,那么再改为

1
2
λ grep "2A1             Rx" test.asc |tail -n 1
3096.986719 1 2A1 Rx d 8 02 00 00 00 00 3F 2E 56 Length = 231910 BitCount = 120 ID = 673

嗯,没有错误了,下面再套个循环即可

实现

for 负责循环,grep 负责查找,tail 负责输出一行内容

1
2
3
4
5
6
7
8
9
10
11
12
13
$ for i in `ls *.asc` ; do grep "2A1             Rx"    $i |tail -n 1 ; done
3096.986719 1 2A1 Rx d 8 02 00 00 00 00 3F 2E 56 Length = 231910 BitCount = 120 ID = 673
3097.043563 1 2A1 Rx d 8 02 00 00 00 00 7E 56 B9 Length = 229925 BitCount = 119 ID = 673
3097.081294 1 2A1 Rx d 8 02 00 00 00 00 BD 84 F7 Length = 231910 BitCount = 120 ID = 673
3096.970488 1 2A1 Rx d 8 02 00 00 00 00 FC B6 34 Length = 229910 BitCount = 119 ID = 673
3096.774315 1 2A1 Rx d 8 02 00 00 00 01 3B E1 8D Length = 231910 BitCount = 120 ID = 673
3096.521664 1 2A1 Rx d 8 02 00 00 00 01 7B 12 CA Length = 229910 BitCount = 119 ID = 673
3096.605671 1 2A1 Rx d 8 02 00 00 00 01 BA 44 07 Length = 233910 BitCount = 121 ID = 673
3096.527474 1 2A1 Rx d 8 02 00 00 00 01 F9 75 44 Length = 231910 BitCount = 120 ID = 673
3096.409386 1 2A1 Rx d 8 02 00 00 00 02 38 A6 81 Length = 229910 BitCount = 119 ID = 673
3096.151840 1 2A1 Rx d 8 02 00 00 00 02 77 D7 BE Length = 231925 BitCount = 120 ID = 673
3096.222497 1 2A1 Rx d 8 02 00 00 00 02 B6 F1 59 Length = 231910 BitCount = 120 ID = 673
3095.960612 1 2A1 Rx d 8 02 00 00 00 02 F6 22 96 Length = 227910 BitCount = 118 ID = 673

使用一个 for 循环,接收 ls 命令的结果,再逐个套用 grep 的查询,就把所有的文件结果查完了,非常简单方便

附录

以上过程在 Windows 系统上完成

调试阶段,使用的命令行窗口工具为 cmder,自带 Linux 命令和常用的全家桶工具,可以直接在命令行里使用

实现阶段,使用的是 cygwin 工具,原因是 Windows 的命令行也有 for 循环,cmder 会自动优先使用 Windows 下的,导致上面的命令报错无法使用