[root@akuilinux01 shellXT]# vim count_words.sh
#!/bin/bash
#定义函数count,用来统计一个文件中的字数
count(){
#函数需要一个参数才可以被正确调用
if [ $# != 1 ]
then
echo "Need one file parameter to work!"
exit 1 ;
fi
#删除标点符号和特殊字符
#构建一个很长的管道命令,每一段都单独写在一行中,增加可读性
tr ‘+-=*.,;:{}()#!?<>"\n\t‘ ‘ ‘ < $1 | #把所有大小写字母转换为小写字母
tr ‘A-Z‘ ‘a-z‘ | #把连续重复的空格符替换为一个空格符
tr -s ‘ ‘ | #把空格符转换为换行符
tr ‘ ‘ ‘\n‘ | #把相同的单词放到一起
sort | #删除重复单词,并进行统计
uniq -c | #根据重复的次数进行排序
sort -rn
}
echo
echo "This script can count words of aspecified file."
#使用空命令冒号构建无限循环
while :
do
read -p "Enter the file path(or quit):"
case "$REPLY" in
[Qq] |[Qq][Uu][Ii][Tt])
echo "Bye."
#在输入大、小写quit时,退出
exit 0
;;
*)
#判断输入的时一个可读的普通文件,并且内容不为空
if [ -f "$REPLY" ] && [ -r "$REPLY" ] && [ -s "$REPLY" ]
then
#当用户输入一个合法文件时
#调用count函数统计文件的单词个数
count "$REPLY"
else
#如果输入了非法文件,显示不能处理它
echo "$REPLY can not be dealed with."
fi
;;
esac
done
exit 0
## 运行结果
[root@akuilinux01 shellXT]# sh count_words.sh
This script can count words of aspecified file.
Enter the file path(or quit):/tmp/shellXT/words.txt
5 name
5 is
4 my
3 akui
1 akui][my
1 akui\
Enter the file path(or quit):
## 讨论
- uniq命令可以统计多个相同行中的一行,-c选项用来统计相邻行的重复次数,在本题中可以使用uniq -c命令统计每一个单词的重复次数,但前提是让每一个单词都占用单独的一行
- 要把每个单词单词单独放一行,需要先把所有的单词(包括相同的行和相同的单词)紧密的连接在一起
- tr命令可以转换或删除输入数据中的字符,使用tr命令把文件中的特殊符号转换为空格符,再把大写字母转换为小写,为了避免多个空格影响空行的统计,还需要把重复的空格符转换为一个空格符,最后还需要把所有的空格符转换为换行符,这样就把每一个单词都单独放在一行中
- 再通过sort命令把相同的单词放到一起
- 这样就能使用uniq -c命令统计出每个单词的次数了
- 最后在用sort -rn从大到小排序
- 为了能让脚本有更加清晰的模块结构,我们把关键的操作定义在count()函数中。脚本执行以后,首先会打印一些信息告诉用户脚本的作用,然后会执行while循环不停的接受用户输入的路径。
- while语句测试的条件为bash的内建命令:。冒号命令是一个空命令,它不会做任何事情并且永远返回真,这样while循环是一个无限循环,会不停的询问用户所要统计的文件。在循环体中通过read命令得到用户的输入,并通过case语句的匹配功能判断用户是否输入了字符q或字符串quit,如果是则执行exit命令退出脚本,否则脚本会把这个输入的字符串当成文件路径。
- 通过if语句检测是否存在这样的一个可读文件,如果存在就以这个路径作为参数,调用函数count()来统计文件中的单词个数,如果不存在就显示相应信息并进入下一次while循环,重复之前的步骤
- 假如用户输入了一个合法的文件,则脚本会以这个文件的路径作为参数调用函数count。在函数体中首先检查函数调用时是否指定了参数,即测试特殊变量$#是否等于1,如果没有指定参数,脚本会输出错误信息并直接终止运行,如果以一个文件路径作为参数调用count函数,这个文件路径会被保存在位置变量$1中,可以看到第一个tr命令通过标准输入重定向从$1中读取文件的数据。
原文地址:http://blog.51cto.com/akui2521/2090958