#!/bin/bash
# getip.sh -- get an usable ipaddress automatically
# author: falcon
# update: Tue Oct 30 23:46:17 CST 2007
# set your own network, default gateway, and the time out of "ping" command
net="192.168.1"
default_gateway="192.168.1.1"
over_time=2
# check the current ipaddress
ping -c 1 $default_gateway -W $over_time
[ $? -eq 0 ] && echo "the current ipaddress is okey\!" && exit -1;
while :; do
# clear the current configuration
ifconfig eth0 down
# configure the ipaddress of the eth0
ifconfig eth0 \
$net.$(($RANDOM /130 +2)) \
up
# configure the default gateway
route add default gw $default_gateway
# check the new configuration
ping -c 1 $default_gw -W $over_time
# if work, finish
[ $? -eq 0 ] && break
done
说明:如果网关地址不是1,那么用ifconfig配置地址时不能配置为网关地址,否则你的IP地址将和网关一样,导致整个网络出现问题。
4. 产生一序列数
其实我们通过一个循环就可以产生一序列数,但是有相关的小工具为什么不用呢!seq就是这么一个小工具,它可以产生一序列数,你可以指定数的递增间隔,也可以指定相邻两个数之间的分割符。
4.1 概要示例:演示seq,打印一序列数
Quote: |
$ seq 5 1 2 3 4 5 $ seq 1 5 1 2 3 4 5 $ seq 1 2 5 1 3 5 $ seq -s: 1 2 5 1:3:5 $ seq 1 2 14 1 3 5 7 9 11 13 $ seq -w 1 2 14 01 03 05 07 09 11 13 $ $ seq -s: -w 1 2 14 01:03:05:07:09:11:13
|
补充:在bash版本3中,在for循环的in后面,可以直接通过{1..5}更简洁地产生自1到5的数字(注意,1和5之间只有两个点),例如:
Quote: |
$ for i in {1..5}; do echo -n "$i "; done 1 2 3 4 5
|
4.2 统计指定字符串(这里以单词为例)的个数
这个灵感来自《高级Bash脚本编程指南》“混杂命令”seq的实例之“字母统计”和CU上一篇统计字母和数字个数的帖子。
4.2.1 首先,我们统计某个文件中所有单词的个数。这里的单词我定义为:由字母组成的单个或者多个字符序列。所以,可以这样实现。
说明:为了方便演示,这里采用我的上一篇转载的日志happiness quotations里头的内容,请把内容复制下来保存为text文件。
Quote: |
//统计每个单词出现的次数 $ cat text | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | uniq -c //统计出出现频率最高的前10个单词 $ cat text | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | uniq -c | sort -n -k 1 -r | head -10 45 is 40 to 32 of 31 a 28 and 27 the 25 happiness 19 happy 18 it 18 in
|
说明:
cat text: 显示text文件里的内容
sed -e "s/[^a-zA-Z]/\n/g": 把非字母的字符全部替换成空格,这样整个文本只剩下字母字符
grep -v ^$:去掉空行
sort: 排序
uniq -c:统计相同行的个数,即每个单词的个数
sort -n -k 1 -r:按照第一列(-k 1)的数字(-n)逆序(-r)排序
head -10:取出前十行
4.2.2 接着我们统计指定单词的个数,即输入需要统计的单词,并返回每个单词的个数。
可以考虑采取两种办法:
第一种:只统计那些需要统计的单词
第二种:用上面的算法把所有单词的个数都统计出来,然后再返回那些需要统计的单词给用户
不过,这两种办法都可以通过下面的结构来实现。
Code:
#!/bin/bash
# statistic_words.sh
if [ $# -lt 1 ]; then
echo "ERROR: you should input 2 words at least";
echo "Usage: basename $0 FILE WORDS ...."
exit -1
fi
FILE=$1
((WORDS_NUM=$#-1))
for n in $(seq $WORDS_NUM)
do
shift
cat $FILE | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | grep ^$1$ | uniq -c
done
说明:
if 条件部分:要求用户输入至少两个参数,第一个是需要统计单词的文件名,第二之后的所有参数是需要统计的单词。
FILE=$1: 获取文件名,即脚本之后的第一个字符串。
((WORDS_NUM=$#-1)):获取单词个数,即总的参数个数($#)减去那个文件名参数(1个)
for 循环部分:首先通过seq产生需要统计的单词个数序列,shift是shell内置变量(请通过help shift获取帮助),它把用户从命令行中传入的参数依次往后移动位置,并把当前参数作为第一个参数即$1,这样通过$1就可以遍历用户所有输入的单词 (仔细一想,这里貌似有数组下标的味道)。你可以考虑把shift之后的那句替换成echo $1测试shift的用法。
演示:
Quote: |
$ chmod +x statistic_words.sh $ ./statistic_words.sh text is Action happy 45 is 1 Action 19 happy
|
采用第二种办法,我们只需要修改shift之后的那句即可。
Code:
#!/bin/bash
# statistic_words.sh
if [ $# -lt 1 ]; then
echo "ERROR: you should input 2 words at least";
echo "Usage: basename $0 FILE WORDS ...."
exit -1
fi
FILE=$1
((WORDS_NUM=$#-1))
for n in $(seq $WORDS_NUM)
do
shift
cat $FILE | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | uniq -c | grep " $1$"
done
演示:
Quote: |
$ ./statistic_words.sh text is Action happy 45 is 1 Action 19 happy
|
说明:很明显,采用第一种办法效率要高很多,因为第一种办法提前找出了需要统计的单词,然后再统计,而后者则不然。实际上,如果使用grep的-E选项,我们无须引入循环,而用一条命令就可以搞定:Quote: |
$ cat text | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | grep -E "^Action$|^is$" | uniq -c 或者 $ cat text | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | egrep "^Action$|^is$" | uniq -c [/code] 所以,可见这些命令sed,grep,uniq,sort是多么有用,它们本身虽然只完成简单的功能,但是通过一定的组合,就可以实现你想要实现的功能啦。对了,统计单词还有个非常有用的命令wc -w,需要用到的时候也可以用它。
补充:在《高级Bash脚本编程指南》一书中还提到jot命令和factor命令,由于我机器上没有,所以没有测试,factor命令可以产生某个数的所有素数。如: [quote] $ factor 100 100: 2 2 5 5
|
5. 总结
到这里,shell编程范例之数值计算就结束啦。该篇主要介绍了:
* shell编程中的整数运算、浮点运算、随机数的产生、数列的产生
* shell的内置命令、外部命令的区别,以及如何查看他们的类型和帮助,关于内置命令和外部命令的比较,请参考:
* shell脚本的几种执行办法
* 几个常用的shell外部命令:sed,awk,grep,uniq,sort等
* 范例:数字递增;求月均收入;自动获取IP地址;统计单词个数
* 其他:相关的用法,比如命令列表,条件测试等,在上述范例中都已经涉及,请认真阅读之
如果您有时间,请温习之。
6. 参考资料和推荐资料
[1] 高级Bash脚本编程指南
[2] shell十三问
[3] shell基础十二篇
[4] 在linux下学习和工作
[5] 在linux下更高效的工作
[6] SED手册
[7] AWK使用手册
[8] 几个shell讨论区
兰大开源社区:
LinuxSir.org:
ChinaUnix.net:
如果合适,建议直接找对应的英文原版阅读!
关于不同类型的数值常量(如八进制、16进制的表示等)的表示,请参考 《高级Bash脚本编程指南》
东西总是学不完,大伙继续,这里是几个好去处,一天看上几篇,保证受益不少:
[1] linuxsir.org Shell版精华
[2] chinaunix.net Shell版综合水平测试
[3] linuxsir.org Shell技巧交流区
[4] linuxsir.org Shell脚本欣赏区
用bc计算器计算“Unix高级编程”第一章课后系统最后两个题目的计算过程:
Quote: |
// 1.5 若日历存放在带符号的32位整数中,那么到哪一年它将溢出? $ echo "(2^31)/(360*24*60*60)+1970-1" | bc 2038 // 1.6 若进程时间存放在带符号的32位整数中,而且每秒为100滴答,那么经过多少天后,该时间值将会溢出。 $ echo "2^31/(100*24*60*60)" | bc 248
|
以上两道题需要明白两个概念:
第一就是Unix时间存放的是从1970年1月1日到现在的秒数,第二格式进程时间存放的是进程运行到现在的滴答数。
如果有时间,这里头的一些资料还是值得您仔细阅读的:
"developerWorks 中国 | Shell、Shell 脚本编写、命令行、相关工具及技巧"
http://www.ibm.com/developerworks/cn/linux/shell/index.html
需要补充的是,日期和时间是一个很好的随机数,它是一个在永恒变化的东西,所以,你无须担心存在重复,它很适合用于生成一些临时文件。具体用法见man date,常用的有date +%s
刚从看到有趣的一点,用(())加#还可以转换进制。
例如:
Quote: |
# echo "$(( 8#11 ))" 9
|
即1*8^0 + 1*8^1 = 9
关于间接变量的引用问题:
例如:
Quote: |
$ a=b $ b=c $ echo $a b $ echo $b c $ eval echo \$$a c $ echo ${!a} c
|
${!a}提供了一种非常方便的间接变量引用办法,参考: