第二章:Improving On User Commands--21.在手册页的数据库中搜索
Unix的man命令有一个非常有用的选项,它产生一个手册页的列表,该列表的描述会包括特定单词。通常这个功能用man -k来实现,但它同样也可以通过apropos或是whatis命令实现。
使用man命令来搜索一个词是很有用的,但这只完成了一半,因为一旦你得到了一系列的匹配,你可能仍然会觉得你自己要执行一个强力搜索,一次一个手册页的来查找你所需要的特定命令,
作为一个小巧的备选项,这个脚本生成了一个可能的手册页列表,它匹配一个特定的模式,然后接着就搜索每一个匹配到的手册页来查找第二个模式。为了给输出一点约束,它也允许用户指定要搜索哪个手册页。
小贴士: man手册页是分数字组成的:1 = 用户命令, 3 = 库函数, 8 = 系统工具等等。你可以通过man intro来搞清楚你的系统组成体系。
代码:
#!/bin/sh # findman.sh -- 指定一个模式以及手册页号码 # 从有关的手册页中显示所有匹配该模式的内容 match1="/tmp/$0.1.$$" matches="/tmp/$0.$$" manpagelist="" trap "rm -f $match1 $matches" EXIT # 捕获到脚本正常退出的话,就删除这2个临时文件 case $# in 3) section="$1" cmdpat="$2" manpagepat="$3";; 2) section="" cmdpat="$1" manpagepat="$2";; *) echo "Usage: `basename $0` [section] cmdpattern manpagepattern" >&2 exit 1 esac if ! man -k "$cmdpat" | grep "($section" > $match1; then echo "No matches to pattern \"$cmdpat\". Try something broader?" >&2 exit 1 fi cut -d\( -f1 < $match1 > $matches cat /dev/null > $match1 # 清空文件 for manpage in $(cat $matches) do manpagelist="$manpagelist $manpage" man $manpage | col -b | grep -i $manpagepat | \ sed "s/^/${manpage}:/" | tee -a $match1 # sed的目的是在行首添加要查找的命令名 done # tee命令在写入文件的同时写到标准输出,-a是追加写模式 if [ ! -s $match1 ]; then cat << EOF Command pattern "$cmdpat" had matches, but within those there were no matches to your man page pattern "$manpagepat". Man page checked: $manpagelist EOF fi exit 0
脚本如何运行:
这个脚本并没有它第一眼看上去的那样简单。它利用了这样一个事实:命令的返回码取决于命令的执行情况。下面这行代码就是利用这点来判断是否有匹配变量cmdpat的值。grep命令的返回码就是重点:
if ! man -k "$cmdpat" | grep "($section" > $match1; then
如果grep没有查到符合条件的结果,它就会返回一个非0值(要知道,if判断成功的条件是它的条件是0值)。所以,我们压根不需要看$match1是不是一个大小大于0的文件。这种方法,可以较快速的达到我们的要求。
$match1文件中的每一行,都具有如下的格式:
httpd (8) - Apache hypertext transfer protocol server
cut -d\( -f1 的意思是以左括号为分隔符,然后只取得第一个域。一旦生成了匹配命令文件,下面就可以在每个命令的手册页中搜索manpagepat了。为了搜索手册页,内部的演示格式必须要格式化好,在这儿使用了 col -b。(注,col是过滤控制字符命令,-b参数是过滤掉所有的控制字符)
为了确保能够生成有效的错误信息,即可以查到该命令,但是查不到manpagepat时,会报一个错误信息,使用下面的一行代码,它把输出追加到一个临时文件中,同时打到标准输出:
sed "s/^/${manpage}:/" | tee -a $match1
然后if的! -s测试表示如果$match1这个输出文件的大小为0,那就显示错误信息。
运行脚本:
注意该脚本运行时需要的参数,最多3个,最少2个。
运行结果:
注,书上用的是测试httpd.conf,老七的机器上并没有这个模块。所以简单测试下awk好了。
先用原先的man -k pgawk gawk [pgawk] (1) - pattern scanning and processing language
再用该脚本测试下3个参数齐全的情况:
./findman.sh 1 awk pgawk gawk: pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ... gawk: pgawk [ POSIX or GNU style options ] [ -- ] program-text file ... gawk: Pgawk is the profiling version of gawk. It is identical in every way gawk: sion of the program. When run with pgawk, the profile contains gawk: pgawk accepts two signals. SIGUSR1 causes it to dump a profile and gawk: pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ... gawk: pgawk [ POSIX or GNU style options ] [ -- ] program-text file ... gawk: Pgawk is the profiling version of gawk. It is identical in every way gawk: sion of the program. When run with pgawk, the profile contains gawk: pgawk accepts two signals. SIGUSR1 causes it to dump a profile and
再测试下2个参数的情况:
./findman.sh awk pgawk gawk: pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ... gawk: pgawk [ POSIX or GNU style options ] [ -- ] program-text file ... gawk: Pgawk is the profiling version of gawk. It is identical in every way gawk: sion of the program. When run with pgawk, the profile contains gawk: pgawk accepts two signals. SIGUSR1 causes it to dump a profile and gawk: pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ... gawk: pgawk [ POSIX or GNU style options ] [ -- ] program-text file ... gawk: Pgawk is the profiling version of gawk. It is identical in every way gawk: sion of the program. When run with pgawk, the profile contains gawk: pgawk accepts two signals. SIGUSR1 causes it to dump a profile and
最后,再测试下打印错误信息的情况:
./findman.sh 1 awk why Command pattern "awk" had matches, but within those there were no matches to your man page pattern "why". Man page checked: a2p gawk gawk [pgawk] igawk states
最后总结下这个脚本。这个脚本的目的是深入到手册页的数据库中,然后按照自己的要求定制查询内容,显示查询结果或者错误信息。最后那个参数就是你要看的命令中要有的内容。这个脚本同样显示出了shell的强大之处,在unix的世界中,想怎么玩都行呀。