linux从入门到精通读薄(五)grep/find/sort
rockety
|
1#
rockety 发表于 2003-11-06 17:29
linux从入门到精通读薄(五)grep/find/sort
第五章 工具和实用程序
5-1 搜索和排序 5-1-1 grep 它是通用规则表达式分析程序(General Regular Expression Parser)的缩写。 grep命令可以在它的输入中搜索指定的字符串模式(Pattern)。输入中所有包含指定字符串模式的行组成grep命令的输出。 例如,要找出carey用户是否在系统中登录,只要在passwd文件中搜索这一用户名称的字符串: $ grep carey /etc/passwd carey:…… grep产生的输出表明,在系统中有carey用户。 grep命令用于规则表达式中的基本特殊字符集(B表示基本E表示扩充): ^ B 在每行的开始进行匹配; $ B 在每行的末尾进行匹配; \< B 在字的开始进行匹配; \> B 在字的末尾进行匹配; . B 对任何单个字符进行匹配; [str] B 对str中的任何单个字符进行匹配; [^str] B 对任何不在str中的单个字符进行匹配; [ a-b] B 对a到b之间的任何字符进行匹配; \ B 抑止后面的一个字符的特殊含义; * B 对前一项(item)进行0次或多次重复匹配; + E 对前一项进行1次或多次重复匹配; ? E 对前一项进行0次或1次重复匹配; {j} E 对前一项进行j次重复匹配; {j,} E 对前一项进行j次或更多次重复匹配; {,k} E 对前一项最多进行k次重复匹配; {j,k} E 对前一项进行j到k次重复匹配; slt E 匹配s项或t项中的一项; (exp) E 将exp作为单项处理 不要忘记,在vi和grep中使用规则表达式的主要差异。在vi编辑程序中,输入的任何字符只被编辑程序本身看到和使用。而grep命令看到的任何参数必须先经过shell。由于在规则表达式中使用的一些特殊字符对shell同样具有特殊含义,要告诉shell:不管这些字符。这样,grep才有机会见到它们。用引用方法实现这一点。回到前面的例子,搜索passwd文件找登录名。现在,形成表示public不是有效登录名的grep命令行 将是一件容易的事: $ grep '^public' /etc/passwd 前面的^字符强制grep命令只在每行的开关找public。整个搜索模式(pattern)用单引号括起来,使shell不理会它们。shell只将单引号去掉,将搜索模式送给grep命令。 grep命令的有用的开关如下: -E 用扩充规则表达式进行模式匹配; -i 不区分大小写; -n 在每一输出行前显示文件内的行号; -q 与其他命令一起使用时,抑止输出显示; -s 抑止文件的出错信息; -num 在每一匹配行前后各显示num行。 5-1-2 find命令 find命令的主要作用是对树形目录层次结构进行彻底检查。从给定的起点开始能过目录的所有分支一直检查到结点。可以在命令中指定一组操作,对find命令发现的每个文件和子目录进行操作。 find命令最常用来产生给定目录下的所有文件和子目录清单: $ cd $ find . -print ./backup ./backup/motd.bak …… find的两个参数是点和-print,它们告诉find命令从当前目录开始搜索,并显示它发现的所有文件和子目录名称。 find命令输出的文件名清单写到标准输出设备,因而也可以通过管道供其他命令使用: $ find . -print | grep passwd ./backup/passwd.bak ./text/passwd find命令的一般格式: find pathname -expressions 其中pathname可以是任何目录路径名清单,对清单中每个目录进行递归搜索来产生文件名输出。而expressions则是任何表达式的清单,用来定义条件和对每个被发现的文件进行的操作。如果没有指定pathname,默认值是当前目录。如果没有指定-expressions,默认值是-print。 find命令使用三种表达式:选项,条件和操作。每个表达式都送回一个值。这个值是真是假取决于使用的表达式和对它的评价方式。当find命令具有一个以上的表达式时,表达式之间用逻辑操作符来控制它们的执行。 给出表达式e1和e2: e1 -a e2 仅当e1为真时,对e2求值; e1 e2 同上; e1 -o e2 仅当e1为假时,对e2求值; e1 , e2 对两个表达式都求值,先e1,后e2。 复合表达式的值是最后实际求值的表达式的返回值。由两个以上表达式组成的复合表达式的求值按自左至右的顺序进行,除非括号()改变了求值顺序。如: e1 -o e2 -a e3等价于(e1 -o e2) -a e3而不同于e1 -o (e2 -a e3) 还可以使用逻辑非操作符: ! e1 当e1为假时,结果为真;反之,当e1为真时,结果为假。 可以和find命令一起使用的一些表达式的类型和它的返回值: -mount 选项表达式,用来防止搜索范围超出当前文件系统的边界。返回值常为真。 -group grp 条件表达式,检查当前文件是否具有与grp相同的GID或组名。一致为真,否则为假。 -name pattern 条件表达式,检查文件名是否和模式pattern相同。pattern可以用规则表达式给出。必要时使用引号。当文件名与pattern一致时值为真,否则为假。 -type t 条件表达式,检查当前文件的类型是否是t。对目录讲,t值可以是d。对普通文件讲,t值可以是f。对连接讲,t值可以是l等等。如果是,值为真,否则为假。 -user usr 条件表达式,检查当前的文件的所有者或UID是否是usr。如果两者一致,返回真值,否则为假。 -exec cmd; 操作表达式,用来执行cmd命令。如果要将当前的文件名传送给命令,应该加{}标记,分号用来表示cmd的结束,并和后面可能出现的表达式分开。如果成功地执行了cmd,返回真值,否则为假。 -print 操作表达式,将当前的文件名送到标准输出设备显示,返回值常为真。 注意:find命令不允许超越系统对文件和目录的权限设置。因此,如果让find命令从一个目录开始搜索,而这个目录下有不能访问的子目录,find命令每遇到一个这样的目录,将向标准输出设备传送一条错误信息。由于默认的标准出错信息输出设备是屏幕,如果使用了-print操作,可能实际输出许多出错信息,如: $ find / -name passwd -pint find:/var/spool/cronermission denied find:/var/spool/atjobsermission denied …… /usr/bin/passwd …… 最简单的做法是抑止这些不必要的输出,将它们重定向到一个文件中去。系统专门提供了一个文件,可以将不需要的东西送到那里去: /dev/null 这是特殊安排的文件,任何时候你将不需要的东西写到那里,相当于将它们抛弃。当试图从那里读取字符时,经常得到的是文件结束的指示。而不会读到任何其他东西。 利用null文件来抑止find命令的出错信息输出,下例就可以写成: $ find / -name passwd -print 2>/dev/null /usr/bin/passwd /etc/passwd 本例中用了两个find命令的表达式,它们之间没有操作符(默认的操作符是-a)。对这一默认的操作符讲,find命令每产生一个文件名,先对第1表达式求值,仅当第一表达式的结果为真时,才用到第2表达式。本例中第1表达式是-name passwd。它从find命令提供的路径名后面找文件名passwd。如果两者相同,它返回真值,否则返回假值。这一返回值决定是否用到第2表达式。如果第1表达式返回值为真,第2表达式(-print)就将find发现的当前路径名送到标准输出设备去显示。总的结果是:将find命令发现的后面带有passwd文件名的所有路径名显示出来。 可以用这种方式单独使用find命令找出路径名清单,不必像前面的例子那样通过管道使用grep命令。但是两者是有差别的,-name表达式只在路径名的末尾找完整的字,而grep命令搜索子字符串(substring): $ cd $ find . -name motd -print 2>/dev/null ./text/motd $ find . -print 2>/dev/null | grep motd ./text/motd ./backup/motd.bak 更复杂一点的例子:如何产生一个目录子树(directory subtree)下的所有包含特定字符串的普通文件的路径名清单呢? 一种可能的解决办法是用find命令产生路径名,然后用grep命令检查这些文件中是否包含特定字符串,并显示所有包含特定字符串的文件的路径名。唯一的问题是grep命令同时在标准输出设备上输出它发现的行和错误信息。克服这一问题的一种办法是将标准输出和出错误信息都重新定向到/dev/null。但grep命令提供了更简单的办法,可以用命令开关抑止这些输出: $ find /etc -type f -exex grep -q -s mycroft {} \; -print /etc/HOSTNAME /etc/hosts /etc/lilo.conf 这里用find命令产生/etc目录下所有路径名的清单,然后对第1表达式-type f求值。如果当前路径名属于普通文件,送回真值。然后对所有普通文件执行第2表达式(-exec grep -q -s mycroft {}\ ;),在每个普通文件中搜索mycroft字符串。不要忘记,在执行grep命令时,{}已用当前路径名代替,-q和-s开关使grep命令不显示标准输出和出错信息。所以,这一表达式只用来产生真值或假值,用来决定是否执行第3表达式。第2表达式中分号前面的反斜杠用来引用分号,使shell不加改变地将它传送给find命令,如果第2表达式返回真值,将执行-print表达式,它的作用是显示当前的路径名。 5-1-3 sort sort命令是一个过滤程序。它将输入的正文中的行按命令中指定的顺序进行排序,并将排序结果送给标准输出设备。 可以选择sort使用的字段分隔符,默认为空白字符(如空格,Tab等)。 例如对虚构口令文件pw.test: root:awmku76tr43d6:0:0::/root/:/bin/sh pc:bdhd74hs9jh3h:50:50::/usr1/pc:/bin/bash carey:esJ9ohd8HH89i:501:50::/usr1/carey:/bin/bash mot:dhjd83kjdJS6D:1500:60::/usr1/mot:/bin/bash grex:cj8AjoWE8h8fs:1500:60::/usr1/mot:/bin/sh $ sort pw.test carey:esJ9ohd8HH89i:501:50::/usr1/carey:/bin/bash grex:cj8AjoWE8h8fs:1500:60::/usr1/mot:/bin/sh mot:dhjd83kjdJS6D:1500:60::/usr1/mot:/bin/bash pc:bdhd74hs9jh3h:50:50::/usr1/pc:/bin/bash root:awmku76tr43d6:0:0::/root/:/bin/sh sort命令可以从命令行中指定的任何文件中接受输入。如果没有指定文件,则从标准输入设备接受输入。 sort命令的一些最有用的开关: -b 不管排序键前面的空格字符; -f 不区分大小写; -n 将排序键作为数字而不作为正文; -r 按从高到低的顺序而不是从低到高的顺序进行排序; -o file 将输出送到file而不是送到标准输出设备; -t s 用s代替空白字符作为字段分隔符; -k s1,s2 用s1字段到(s2-1)字段作为排序键。 -k开关的详解: -k3 排序键从字段3开始到行的结束; -k3,6 排序键由行中第3,4,5字段组成; -k4,5 -k1,3 排序键是第4字段和第1,2字段; -k3.3,4 排序键是第3字段,但去掉字段中前2个字符; -k3.2,3.6 排序键是第3字段,从第2字符开始到第5字符共4个字符。 如果想访问文件中的各个字段,首先应该将它的字段分隔符改为需要的类型。下面用第3字段中的UID对pw.test文件排序: $ sort -t: -k3,4 pw.test root:awmku76tr43d6:0:0::/root/:/bin/sh grex:cj8AjoWE8h8fs:1500:60::/usr1/mot:/bin/sh mot:dhjd83kjdJS6D:1500:60::/usr1/mot:/bin/bash pc:bdhd74hs9jh3h:50:50::/usr1/pc:/bin/bash carey:esJ9ohd8HH89i:501:50::/usr1/carey:/bin/bash 再复杂一点儿,当第3字段不能确定行的顺序时,接着用第7字段进行排序: $ sort -t: -k3,4 -k7 pw.test root:awmku76tr43d6:0:0::/root/:/bin/sh mot:dhjd83kjdJS6D:1500:60::/usr1/mot:/bin/bash grex:cj8AjoWE8h8fs:1500:60::/usr1/mot:/bin/sh pc:bdhd74hs9jh3h:50:50::/usr1/pc:/bin/bash carey:esJ9ohd8HH89i:501:50::/usr1/carey:/bin/bash 上面的例子似乎没有正确按UID的值进行排序。关键字的顺序是0,1500,1500,500,501。这是因为sort将UID的内容作为字而不是作为数来对待。解决的办法是使用-n开关: $ sort -t: -n -k3,4 -k7 pw.test root:awmku76tr43d6:0:0::/root/:/bin/sh pc:bdhd74hs9jh3h:50:50::/usr1/pc:/bin/bash carey:esJ9ohd8HH89i:501:50::/usr1/carey:/bin/bash grex:cj8AjoWE8h8fs:1500:60::/usr1/mot:/bin/sh mot:dhjd83kjdJS6D:1500:60::/usr1/mot:/bin/bash 用口令字段的第1,2字符作为排序键: $ sort -t: -r -k2.1,2.3 pw.test carey:esJ9ohd8HH89i:501:50::/usr1/carey:/bin/bash mot:dhjd83kjdJS6D:1500:60::/usr1/mot:/bin/bash grex:cj8AjoWE8h8fs:1500:60::/usr1/mot:/bin/sh pc:bdhd74hs9jh3h:50:50::/usr1/pc:/bin/bash root:awmku76tr43d6:0:0::/root/:/bin/sh 可以将sort输出重新定向到管道和文件,但是如下的形式是不被允许的: $ sort somefile >somefile 因为shell在做重新定向的准备工作时会删去你的输入文件。如果你一定要将输出送回同一个文件,就在sort命令中加上-o开关; $ sort somefile -o somefile (待续) |