mp77的UNIX课件笔记(4.b)

二、常用UNIX系统工具

UNIX已拥有许多功能强大的文本编辑器的支持,但对于系统管理人员来说,在shell级直接发送指令并准确获得自己所需的数据——这不失为一种提高效率的好办法。而UNIX附带的文本文件处理程序几乎就能完全实现这些功能,总的来说这些程序有如下特点:

1、默认从标准输入stdin获得数据; 2、当指定文件名时,从文件中获取数据,可以同时指定多个文件; 3、默认在标准输出stdout显示; 下面将对这些直接针对文本进行操作(也可以通过管道重定向自其它位置,后文介绍)的程序逐一进行说明。

1)基本文件操作

more

UNIX最基本的逐屏显示程序,当显示满一屏后最后一行显示为——more——,按任意键后跳至下一屏内容。Linux中还附带了一个功能更为强大的逐屏显示程序:

less

less不仅提供了more独特的逐屏显示功能,还支持键盘的方向键,或者j,k,类似vi的光标定位键以及PageUp、PageDown、Ctrl+F/B、Home和End键。 UNIX中还有一个古老的逐屏显示程序pg,其显示方式与more接近,不过每屏显示后需要输入子命令来选择上滚或下翻操作,功能上与less相近,近年来的发行版中都已经用more/less代替了pg。

cat

cat类似于dos中的type命令,显示文本文件中的内容,其独特之处在于可以利用>运算符将标准输入重定向至文件,直到Ctrl+D结束输入(亦或者是直接指定输入源文件),这是创建新文件一种较为简捷的方法。

echo

将命令行参数输出至标准输出,echo有很多备选选项,可以自由格式化输出结果。例如:

echo abcdABCD | od –x

od即octal dump,八进制输出。也就是说以字节为单位输出相关信息,上例中把echo输出的字符串重定向至od输入串,按字节分析后转换为十六进制打印结果如下: 0000000 6162 6364 4142 4344 0a00

head -linecount

tail -linecount

head和tail分别是查看文件头部/尾部的指定行数,这对程序主要用于查看一些严格按照格式书写的文件——例如公文、邮件等一些相关的设置信息。同时也是为避免查阅超大文件时效率低下的问题(但要真正实现随心所欲查看似乎仍然需要一些技巧)。例如:

netstat –s –p tcp | head –5

netstat指network statistics,是一种统计网络出入、路由表以及当前网络接口状态信息的程序。-s选项指定按协议输出每一条项目的详细数据,-p tcp只显示基于tcp协议的网络连接状态。head -5只显示前五行,即输出头部部分的全局信息。 tail还有一个实用的选项-f(forever),意即实时显示文件新增加的内容,也就是说程序在首次输出完成后并不退出,而是对目标文件继续监听并输出新增内容,除非用户Ctrl+C强行中断程序运行。

wc

列出文件中一共有多少行,多少个单词、字符,当指定的文件数大于1时,最后还列出一个合计。也可以根据选项指定输出信息:-1文件行数;-c文件字符数;-w文件单词数。 当同时处理多个文件时,wc会在最后一行列出每一项的totoal值。

sort

对文件内容进行排序,这种排序对大多数文件来说并没有太大意义,不过对于phonebook或者是checklist来说还算是比较方便的了(尽管现在恐怕已经很少有人再把这样的信息明文写在ASCII文本里了)。主要使用默认选项(按ASCII字符串比较方式)和-n(numberic)按数值均为从小到大排序。

tee

tee其实是英文字母T的单词形式,其形象描述了本程序的功能——将程序输出信息结果保存至指定文件。一般要和管道连用:

who | tee whois.log

2)高级文件操作

接下来的问题涉及到模式搜索的范畴,其最便于使用的工具无非是正则表达式(Regular expression)。但是正则表达式正面临这样一种尴尬:入起门来十分容易,一旦深入研究就变得异常艰难,UNIX就是采用了正则表达式进行文本文件模式匹配的操作系统之一,尽管其起源于编译原理,但又有多少编译大拿真正把正则表达式融会贯通呢? 幸好那些深入的工作离我们尚有不小的距离,能够熟练掌握如今这种被越来越多高级语言推崇的模式匹配工具,就已经足以使我们的技术水平更上数层台阶了。 正则表达式中含有以下六个固定的元字符 . * [ \ ^ $ 元字符代表特殊的含义,如$,只在出现至表达式最尾部时才表示行尾标志,否则与其自身匹配; ^只有出现在表达式最首部时才有特殊意义,否则与其自身匹配; *匹配其前面的单字符出现0次或多次; 句点.匹配任意单个字符(不能为空); \(backslash)即转义字符,可以使元字符直接与自身匹配; [和]搭配使用时定义了一个集合,其可以匹配集合中描述的任意一个字符。 其它符号与自身匹配(在扩展正则表达式和一些其它语言定义的正则表达式中,还包括一些额外的元字符)。需要注意的是,我们知道

ls *.[ch]

是指显示当前目录下后缀为.c和.h文件列表。正则表达式与此处的shell通配符意义并不相同,请读者稍加注意。

现在我们介绍一些建立在正则表达式基础上的文本文件处理程序。

grep pattern file-list

global regular expression print,文本过滤程序,按正则表达式规则,筛选出含有指定模式字符串的文本行。当指定的文件数>1,查找到指定字符串时,整个行,连同文件名一起显示。指定的文件数≤1,只列出含有指定模式的整个行的内容,不显示文件名。 grep在默认情况下是不识别正则表达式的,例如:

grep O_RDWR /usr/include/*.h

显示O_RDWR定义的库文件位置。如果要使用正则表达式,需要在pattern项处用’regular expression’注明。 egrep指扩充的(extended)正则表达式文本过滤程序,本程序比grep多出几个元字符: (),即一个字符串集合,这将更改原有*的定义; +,表示其前面的单字符或串集合出现一次或多次; ?,表示0次或1次出现,比句点.多出对空字符的匹配。 在处理较多批次文件时应当考虑到egrep的算法时间复杂度问题。 fgrep和前两个程序-f选项的功能基本一致,即不带正则表达式的字符串匹配,这是性能最好的一种过滤程序,同时也能快速匹配一些grep/egrep由于自身原因无法匹配的字符串。 以上三个程序都共享一些常用的选项,例如-n每行显示行号;-i忽略字母的大小写;-v显示所有不包含模式的行。

当我们利用grep来建立某些大规模文本过滤规则的时候,有时会要求一些具备精确结果的报表,而这是grep无法实现的功能。但此类功能对于管理员来说并不罕见,因而促使一些功能强大的同类工具的崛起。

awk ‘Program’ file-list

awk是以创建者Aho、Weinberger、Kernighan三人的姓名首字母命名的,其强大之处在于Program部分支持类似C语言、并吸收了许多同期语言优秀的语法规则,且同样识别’regular expression’。另外内置了多个全局变量: NR,No. of Record当前记录的记录号; $0,当前记录; $1,$2,…,当前记录中的域,通常以[{witespace}\t]等为token界限。 FILENAME,当前输入的文件名。 awk甚至还支持C语言中所有的关系运算符。下面给出一些awk程序示例:

date | awk ‘{print $4}’

未指定file-list,未指定条件,输出所有记录中第四个域(即当前系统时间)的内容。

who | awk ‘/^ *zhang/ {printf(“%s ”,$2)}’

未指定file-list,未指定条件,使用正则表达式,输出所有记录中zhang姓用户登录的终端标识。

ls -s | awk ‘$1>2000 {print $2}’

未指定file-list,指定条件为记录中第一域数据大于2000,输出所有记录中相应第二域的内容。 另外,awk提供了-F选项供用户自定义域分隔符,例如:

awk –F : ‘$2 ==〝〞’ /etc/passwd

awk –F : ‘$2 ~/^$/’ /etc/passwd

awk –F : ‘$2 !~/./’ /etc/passwd

awk –F : ‘length($2) == 0’ /etc/passwd

指定file-list为/etc/passwd,指定条件,其中~表示与正则表达式匹配,!~表示与正则表达式不匹配,模式的域分隔符由默认的[{witespace}\t]被用户指定为冒号:,如此以来就可以处理诸如/etc/passwd中的文本结构了。 更令人惊讶的是,awk的-f支持用户载入自己定义的规则和报表格式文件,例如:

cat list.awk

BEGIN { printf(“=====================================\n”) printf(“FILENAME %s\n”, FILENAME) printf(“————————————-\n”)} END { printf(“====================================\n”) } { printf(“%3d: %s\n”, NR, $0) } 其中BEGIN和END分别定义了报表头部和尾部的输出规则,最后是报表内容的输出规则。

awk –f list.awk md5.c


FILENAME md5.c

1: 2: #include “md5.h” 3: 4: / forward declaration / 5: static void Transform (); 6: 7: / F, G, H and I are basic MD5 functions / 8: #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) …… 298: buf[2] += c; 299: buf[3] += d; 300: }

301:

如今awk在开发者的不断发展下功能已经日趋多样化,甚至还拥有了自身独有的程序设计语言——awk语言。 有了awk作为强有力的文本过滤工具,就不能缺少其在shell脚本方面的王牌搭档——sed。 sed全称stream editor,很简单即流编辑器,但其功能绝非简单而已。sed的两种命令方法都与awk类似,且完全可以参考后者:

sed ‘Program’ file-list

sed –f filename file-list

这里只解析sed的一条语句。

tail –f pppd.log | sed ’s/145.37.123.26/QiaoXi/g’

上面这条命令的目的是开启一个pppd连接日志的实时监控窗口,并将其中的IP地址转换为QiaoXi输出。其中s命令是“替换(substitute)”,正slash分割正则表达式,后半部分是替换字符串QiaoXi,g指global flag,使得s命令在一行中遇到多个模式描述的字符串时,都替换为QiaoXi,否则,一行仅替换一次。 事实上awk和sed作为UNIX脚本编程的利器已经雄霸了多年,直到最近二十年,各种继承先哲的OO scripting languages如Perl、Ruby以及Python等异军突起,迅速征服了大量技术人员,后来人们还发现这些脚本语言能很好地用于软件工程快速原型设计。关于awk+sed的脚本编程读者可以参考经典书籍,我们今后也会有shell编程方面的详细讨论。

最后,我们来看两条令人轻松的程序。

tr string1 string2

单纯地将在字符串string1中出现的输入字符被替换为字符串string2中的对应字符。

cat telnos | tr UVX uvx

cat report | tr ‘[a-z]’ ‘[A-Z]’

cat file1 | tr % ‘\012’ //将%改为换行符

cat myap.c | tr ‘\015’ ‘ ’ > myap1.c //将文件中多余的回车改为空格,回车的ASCII码是八进制的015

仍然要注意的是不要漏掉必需的单引号。

cmp file1 file2

顾名思义,cmp就是比较两个任意类型的文件,并将结果输出在标准输出中。默认情况下,当两个文件完全一致时不会返回任何信息,否则返回不一致的行数和首字符位置信息。还有一种基于两个文件不一致性处理的程序diff。

diff file1 file2

11a12,13 diff不仅有对比两个任意文件(包括文本文件、二进制文件等)的功能,而且可以指定对其中不一致的地方进行增删改操作。如以上命令行在file1中的11行之后append文件2中的12、13行,类似的还有c(change)即将file1中的某行替换为file2中的某行,以及d(delete)即删除file1中的指定内容并加入file2中的指定内容。