ack快富循环理财系统循环系统

何不 Ack?Grep, Ack, Ag的搜索效率对比_Linux教程_Linux公社-Linux系统门户网站
你好,游客
何不 Ack?Grep, Ack, Ag的搜索效率对比
作者:董伟明
我(@董伟明9&)经常看到很多程序员, 运维在代码搜索上使用ack, 甚至ag(the_silver_searcher ), 而我工作中95%都是用grep,剩下的是ag。 我觉得很有必要聊一聊这个话题。
我以前也是一个运维, 我当时也希望找到最好的最快的工具用在工作的方方面面。 但是我很好奇为什么ag和ack没有作为linux发行版的内置部分。 内置的一直是grep。 我当初的理解是受各种开源协议的限制, 或者发行版的boss个人喜好。 后来我就做了实验, 研究了下他们到底谁快。 当时的做法也无非跑几个真实地线上log看看用时。 然后我也有了我的一个认识: 大部分时候用grep也无妨, 日志很大的时候用ag。
ack原来的域名是, 现在是。 好吧,其实我理解使用ack的同学, 也理解ack产生的原因。 这里就有个故事。
最开始我做运维使用shell, 经常做一些分析日志的工作。 那时候经常写比较复杂的shell代码实现一些特定的需求。 后来来了一位会perl的同学。 原来我写shell做一个事情, 写了20多行shell代码, 跑一次大概5分钟, 这位同学来了用perl改写, 4行, 一分钟就能跑完。 亮瞎我们的眼, 从那时候开始, 我就觉得需要学perl,以至于后来的python。
perl是天生用来文本解析的语言, ack的效率确实很高。 我想着可能是大家认为ack要更快更合适的理由吧。 其实这件事要看场景。 我为什么还用比较&土&的grep呢?
看一下这篇文章, 希望给大家点启示。不耐烦看具体测试过程的同学,可以直接看结论:
在搜索的总数据量较小的情况下, 使用grep, ack甚至ag在感官上区别不大
搜索的总数据量较大时, grep效率下滑的很多, 完全不要选
ack在某些场景下没有grep效果高(比如使用-v搜索中文的时候)
在不使用ag没有实现的选项功能的前提下, ag完全可以替代ack/grep
PS: 严重声明, 本实验经个人实践, 我尽量做到合理。 大家看完觉得有异议可以试着其他的角度来做。 并和我讨论。
我使用了公司的一台开发机(gentoo)
我测试了纯英文和汉语2种, 汉语使用了的字典, 英语使用了miscfiles中提供的词典
# 假如你是: sudo apt-get install miscfiles
wget https:///fxsjy/jieba/master/extra_dict/dict.txt.big
实验前的准备
我会分成英语和汉语2种文件, 文件大小为1MB, 10MB, 100MB, 500MB, 1GB, 5GB。 没有更多是我觉得在实际业务里面不会单个日志文件过大的。 也就没有必要测试了(就算有, 可以看下面结果的趋势)。用下列程序深入测试的文件:
cat make_words.py
# coding=utf-8
import random
from cStringIO importStringIO
EN_WORD_FILE ='/usr/share/dict/words'
CN_WORD_FILE ='dict.txt.big'
with open(EN_WORD_FILE)as f:
EN_DATA = f.readlines()
with open(CN_WORD_FILE)as f:
CN_DATA = f.readlines()
MB = pow(1024,2)
SIZE_LIST =[1,10,100,500,1024,1024*5]
EN_RESULT_FORMAT ='text_{0}_en_MB.txt'
CN_RESULT_FORMAT ='text_{0}_cn_MB.txt'
def write_data(f, size, data, cn=False):
total_size =0
s =StringIO()
for x in range(10000):
cho = random.choice(data)
cho = cho.split()[0]if cn else cho.strip()
s.write(cho)
s.seek(0, os.SEEK_END)
total_size += s.tell()
contents = s.getvalue()
f.write(contents +'\n')
if total_size & size:
for index, size in enumerate([
MB *1024*5]):
size_name = SIZE_LIST[index]
en_f = open(EN_RESULT_FORMAT.format(size_name),'a+')
cn_f = open(CN_RESULT_FORMAT.format(size_name),'a+')
write_data(en_f, size, EN_DATA)
write_data(cn_f, size, CN_DATA,True)
好吧, 效率比较低是吧? 我自己没有vps, 公司服务器我不能没事把全部内核的cpu都占满(不是运维好几年了)。 假如你不介意htop的多核cpu飘红, 可以这样,耗时就是各文件生成的时间短板。这是生成测试文件的多进程版本:
# coding=utf-8
import random
import multiprocessing
from cStringIO importStringIO
EN_WORD_FILE ='/usr/share/dict/words'
CN_WORD_FILE ='dict.txt.big'
with open(EN_WORD_FILE)as f:
EN_DATA = f.readlines()
with open(CN_WORD_FILE)as f:
CN_DATA = f.readlines()
MB = pow(1024,2)
SIZE_LIST =[1,10,100,500,1024,1024*5]
EN_RESULT_FORMAT ='text_{0}_en_MB.txt'
CN_RESULT_FORMAT ='text_{0}_cn_MB.txt'
inputs =[]
def map_func(args):
def write_data(f, size, data, cn=False):
f = open(f,'a+')
total_size =0
s =StringIO()
for x in range(10000):
cho = random.choice(data)
cho = cho.split()[0]if cn else cho.strip()
s.write(cho)
s.seek(0, os.SEEK_END)
total_size += s.tell()
contents = s.getvalue()
f.write(contents +'\n')
if total_size & size:
_f, size, data, cn = args
write_data(_f, size, data, cn)
for index, size in enumerate([
MB *1024*5]):
size_name = SIZE_LIST[index]
inputs.append((EN_RESULT_FORMAT.format(size_name), size, EN_DATA,False))
inputs.append((CN_RESULT_FORMAT.format(size_name), size, CN_DATA,True))
pool = multiprocessing.Pool()
pool.map(map_func, inputs, chunksize=1)
等待一段时间后,测试的文件生成了。目录下是这样的:
-rw-rw-r--1 vagrant vagrant 2.2KMar1405:25 benchmarks.ipynb
-rw-rw-r--1 vagrant vagrant 8.2MMar1215:43 dict.txt.big
-rw-rw-r--1 vagrant vagrant 1.2KMar1215:46 make_words.py
-rw-rw-r--1 vagrant vagrant 101MMar1215:47 text_100_cn_MB.txt
-rw-rw-r--1 vagrant vagrant 101MMar1215:47 text_100_en_MB.txt
-rw-rw-r--1 vagrant vagrant 1.1GMar1215:54 text_1024_cn_MB.txt
-rw-rw-r--1 vagrant vagrant 1.1GMar1215:51 text_1024_en_MB.txt
-rw-rw-r--1 vagrant vagrant 11MMar1215:47 text_10_cn_MB.txt
-rw-rw-r--1 vagrant vagrant 11MMar1215:47 text_10_en_MB.txt
-rw-rw-r--1 vagrant vagrant 1.1MMar1215:47 text_1_cn_MB.txt
-rw-rw-r--1 vagrant vagrant 1.1MMar1215:47 text_1_en_MB.txt
-rw-rw-r--1 vagrant vagrant 501MMar1215:49 text_500_cn_MB.txt
-rw-rw-r--1 vagrant vagrant 501MMar1215:48 text_500_en_MB.txt
-rw-rw-r--1 vagrant vagrant 5.1GMar1216:16 text_5120_cn_MB.txt
-rw-rw-r--1 vagrant vagrant 5.1GMar1216:04 text_5120_en_MB.txt
$ ack --version # ack在ubuntu下叫`ack-grep`
Running under Perl5.16.3 at /usr/bin/perl
Copyright2005-2013AndyLester.
This program is free software.You may modify or distribute it
under the terms of the ArtisticLicense v2.0.
$ ag --version
ag version 0.21.0
$ grep --version
grep (GNU grep)2.14
Copyright(C)2012FreeSoftwareFoundation,Inc.
LicenseGPLv3+: GNU GPL version 3or later &http://gnu.org/licenses/gpl.html&.
Thisis free software: you are free to change and redistribute it.
Thereis NO WARRANTY, to the extent permitted by law.
WrittenbyMikeHaerteland others, see &http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS&.
为了不产生并行执行的相互响应, 我还是选择了效率很差的同步执行, 我使用了ipython提供的%timeit。 测试程序的代码如下:
import glob
import subprocess
import cPickle as pickle
from collections import defaultdict
'cn':('豆瓣','小明明'),
'en':('four','python')
OPTIONS =('','-i','-v')
FILES = glob.glob('text_*_MB.txt')
EN_RES = defaultdict(dict)
CN_RES = defaultdict(dict)
'en': EN_RES,
'cn': CN_RES
REGEX = re.compile(r'text_(\d+)_(\w+)_MB.txt')
CALL_STR ='{command} {option} {word} {filename} & /dev/null 2&&1'
for filename in FILES:
size, xn = REGEX.search(filename).groups()
for word in IMAP[xn]:
_r = defaultdict(dict)
for command in['grep','ack','ag']:
for option in OPTIONS:
rs =%timeit -o -n10 subprocess.call(CALL_STR.format(command=command, option=option, word=word, filename=filename), shell=True)
best = rs.best
_r[command][option]= best
RES[xn][word][size]= _r
data = pickle.dumps(RES)
with open('result.db','w')as f:
f.write(data)
温馨提示, 这是一个灰常耗时的测试。 开始执行后 要喝很久的茶&
我来秦皇岛办事完毕(耗时超过1一天), 继续我们的实验。
我想要的效果
我想工作的时候一般都是用到不带参数/带-i(忽略大小写)/-v(查找不匹配项)这三种。 所以这里测试了:
英文搜索/中文搜索
选择了2个搜索词(效率太低, 否则可能选择多个)
分别测试&&/&-i&/&-v&三种参数的执行
使用%timeit, 每种条件执行10遍, 选择效率最好的一次的结果
每个图代码一个搜索词, 3搜索命令, 一个选项在搜索不同大小文件时的效率对比
我先说结论
在搜索的总数据量较小的情况下, 使用grep, ack甚至ag在感官上区别不大
搜索的总数据量较大时, grep效率下滑的很多, 完全不要选
ack在某些场景下没有grep效果高(比如使用-v搜索中文的时候)
在不使用ag没有实现的选项功能的前提下, ag完全可以替代ack/grep
渲染图片的gist可以看这里。 它的数据来自上面跑的结果在序列化之后存入的文件。
grep使用简明及正则表达式&
Linux下Shell编程&&grep命令的基本运用
grep 命令详解及相关事例
Linux基础命令之grep详解
设置grep高亮显示匹配项
Linux grep命令学习与总结
本文永久更新链接地址:
相关资讯 & & &
& (09/15/:29)
& (05/17/:06)
& (01月25日)
& (09/13/:07)
& (04/17/:50)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款第三部分.&高级bash脚本编程笔记-进阶&(10,循环和分支)(ASCII表格)
《高级BASH脚本编程》学习记录
10. 循环与分支
10.1. 循环
10.2. 嵌套循环
10.3. 循环控制
10.4. 测试与分支(case与select结构)
对代码块的操作是构造和组织shell脚本的关键. 循环和分支结构为脚本编程提供了操作代码块的工具
10.1. 循环
循环就是迭代(重复)一些命令的代码块, 如果循环控制条件不满足的话, 就结束循环.
for argin [list]
&&& 这是一个基本的循环结构.
它与C语言中的for循环结构有很大的不同.
&&& for argin
command(s)...
在循环的每次执行中, arg将顺序的访问list中列出的变量.
1 for arg in "$var1" "$var2" "$var3" ... "$varN"
2 # 在第1次循环中, arg = $var1
3 # 在第2次循环中, arg = $var2
4 # 在第3次循环中, arg = $var3
6 # 在第N此循环中, arg = $varN
8 # 在[list]中的参数加上双引号是为了阻止单词分割.
list中的参数允许包含通配符.
例子 10-1. 一个简单的for循环
1 #!/bin/bash
2 # 列出所有的行星名称. (译者注: 现在的太阳系行星已经有了变化^_^)
4 for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus
Neptune Pluto
echo $planet # 每个行星都被单独打印在一行上.
11 for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus
Neptune Pluto"
12 # 所有的行星名称都打印在同一行上.
13 # 整个'list'都被双引号封成了一个变量.
echo $planet
每个[list]中的元素都可能包含多个参数. 在处理参数组时, 这是非常有用的. 在这种情况下, 使用set命令(参见 例子
11-15)来强制解析每个[list]中的元素, 并且将每个解析出来的部分都分配到一个位置参数中.
输出结果:
$ bash for1.sh
Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto
例子 10-2. 每个[list]元素中都带有两个参数的for循环
#!/bin/bash
for planet in "Mercury 36" "Venus 67" "Earth 93" "Mars 142"
"Jupiter 483"
$planet&&&
给脚本设置参数!!!
&& echo "$1 $2,000,000 miles from
可以将一个变量放在for循环的[list]位置上.
输出结果:
$ bash for2.sh
Mercury 36,000,000 miles from the sun
Venus 67,000,000 miles from the sun
Earth 93,000,000 miles from the sun
Mars 142,000,000 miles from the sun
Jupiter 483,000,000 miles from the sun
for ss in "pageType chanelNm
把双引号里面的字符串,按照空格设置参数,输出 $1 ~ $9
& echo $1 $2 $3 $4 $5 $6
例子 10-3.
文件信息:对包含在变量中的文件列表进行操作
1 #!/bin/bash
2 # fileinfo.sh
4 FILES="/usr/sbin/accept
5 /usr/sbin/pwck
6 /usr/sbin/chroot
7 /usr/bin/fakefile
8 /sbin/badblocks
/sbin/ypbind"&&&&&&&&&&&&&&&&&&&
# 这是你所关心的文件列表.
10&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 扔进去一个假文件, /usr/bin/fakefile.
14 for file in $FILES
17 if [ ! -e "$file"
]&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 检查文件是否存在.
19 echo "$file does not exist."; echo
continue&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 继续下一个.
23 ls -l $file | awk '{ print $9 " file size: " $5
# 打印两个域.
24 whatis `basename
$file`&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 文件信息.
25&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 注意whatis数据库需要提前建立好.
26&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 要想达到这个目的, 以root身份运行/usr/bin/makewhatis.
如果在for循环的[list]中有通配符 (*和?), 那么将会发生通配(globbing), 也就是文件名扩展.
例子 10-4.
在for循环中操作文件
1 #!/bin/bash
2 # list-glob.sh: 使用"globbing", 在for循环中产生[list]
6 for file in *
7&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# ^& 在表达式中识别文件名匹配时,
8&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#+ Bash将执行文件名扩展.
"$file"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 列出在$PWD(当前目录)中的所有文件.
11&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#& 回想一下,通配符"*"能够匹配所有文件,
& #+ 然而,在"globbing"中,是不能比配"."文件的.
& #& 如果没匹配到任何文件,那它将扩展成自己.
15&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#& 为了不让这种情况发生,那就设置nullglob选项
& #+ (shopt -s nullglob).
17&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#& 感谢, S.C.
22 for file in [jx]*
$file&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 只删除当前目录下以"j"或"x"开头的文件.
25 echo "Removed file \"$file\"".
在一个for循环中忽略in [list]部分的话, 将会使循环操作$@-- 从命令行传递给脚本的位置参数. 一个非常好的例子,
参见例子 A-16. 参见例子 11-16.
例子 10-5. 在for循环中省略in
[list]部分
1 #!/bin/bash
3&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#& 使用两种方式来调用这个脚本, 一种带参数, 另一种不带参数,
4&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#+ 并观察在这两种情况下, 此脚本的行为.
8 echo -n "$a "
11&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#& 省略'in list'部分, 因此循环将会操作'$@'
12&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
#+ (包括空白的命令行参数列表).
也可以使用命令替换来产生for循环的[list]. 参见例子 12-49, 例子 10-10和例子 12-43.
例子 10-6.
使用命令替换来产生for循环的[list]
1 #!/bin/bash
2 # for-loopcmd.sh: 带[list]的for循环,
3 #+ [list]是由命令替换所产生的.
5 NUMBERS="9 7 3 8 37.53"
7 for number in `echo
$NUMBERS`&&&&&&&
# for number in 9 7 3 8 37.53
9 echo -n "$number "
下边是一个用命令替换来产生[list]的更复杂的例子.
例子 10-6.
使用命令替换来产生for循环的[list]
1 #!/bin/bash
2 # for-loopcmd.sh: 带[list]的for循环,
3 #+ [list]是由命令替换所产生的.
5 NUMBERS="9 7 3 8 37.53"
7 for number in `echo $NUMBERS` # for number in 9 7 3 8 37.53
9 echo -n "$number "
例子 10-7.
对于二进制文件的grep替换
$ cat bin-grep.sh
#!/bin/bash
E_BADARGS=65
E_NOFILE=66
if [ $# -ne 2 ]
& echo "Usage: `basename $0` search_string
& exit $E_BADARGS
if [ ! -f "$2" ]
& echo "File \"$2\" does not exist."
& exit $E_NOFILE
IFS=$'\012'
for word in $( strings "$2" | grep "$1" )&
#strings命令列出二进制文件的内容,以十进制可读的形式
& echo $word
例子 10-9.
在目录的所有文件中查找源字串
$ cat findstring.sh
#!/bin/bash
# 在一个指定目录的所有文件中查找一个特定的字符串
directory=/usr/bin/
fstring="Free Software Foundation"
for file in $( find $directory -type f -name '*' | sort )
& strings -f $file | grep "$fstring" | sed -e
"s%$directory%%"
for循环的输出也可以通过管道传递到一个或多个命令中.
例子 10-10.
列出目录中所有的符号链接
$ cat symlinks.sh
#!/bin/bash
directory=${1-`pwd`}
echo "symbolic links in directory \"$directory\""
for file in "$( find $directory -type l )"&
如果没将$( find $directory -type l
)用""引用起来的话,那么将会把一个带有空白部分的文件名拆分成以空白分隔的两部分(文件名允许有空白).即使这里只会取出每个参数的第一个域.
&& echo "$file"
done | sort
38 # Jean Helou建议采用下边的方法:
40 echo "symbolic links in directory \"$directory\""
41 # 当前IFS的备份. 要小心使用这个值.
42 OLDIFS=$IFS
45 for file in $(find $directory -type l -printf "%p$IFS")
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# ^^^^^^^^^^^
47 echo "$file"
48 done|sort
$ find /bin -type l -printf
"%p$IFS"&&&&
以冒号间隔输出,
/bin/ypdomainname:/bin/nisdomainname:/bin/static-sh:/bin/lsmod:/bin/sh.distrib:/bin/lessfile:/bin/sh:/bin/bzfgrep:/bin/rnano:/bin/pidof:/bin/mt:/bin/rbash:/bin/domainname:/bin/bzcmp:/bin/bzegrep:/bin/open:/bin/bzless:/bin/dnsdomainname:/bin/netcat:/bin/nc:
$ find /bin -type l
/bin/ypdomainname
/bin/nisdomainname
/bin/static-sh
/bin/lsmod
/bin/sh.distrib
/bin/lessfile
/bin/bzfgrep
/bin/rnano
/bin/pidof
/bin/rbash
/bin/domainname
/bin/bzcmp
/bin/bzegrep
/bin/bzless
/bin/dnsdomainname
/bin/netcat
有一种非常像C语言for循环的语法形式. 需要使用(()).
例子 10-12.
一个C风格的for循环
1 #!/bin/bash
2 # 两种循环到10的方法.
6 # 标准语法.
7 for a in 1 2 3 4 5 6 7 8 9 10
9 echo -n "$a "
14 # +==========================================+
16 # 现在, 让我们用C风格语法来做相同的事情.
18 LIMIT=10
20 for ((a=1; a &= LIMIT ; a++)) # 双圆括号,
并且"LIMIT"变量前面没有"$".
22 echo -n "$a "
23 done # 这是一个借用'ksh93'的结构.
+=========================================================================+
29 # 让我们使用C语言的"逗号操作符", 来同时增加两个变量的值.
31 for ((a=1, b=1; a &= LIMIT ; a++, b++)) # 逗号将同时进行两条操作.
33 echo -n "$a-$b "
现在, 让我们来看一个"现实生活"中使用for循环的例子.
例子 10-13. 在batch
mode中使用efax
1 #!/bin/bash
2 # Faxing (前提是'fax'必须已经安装好).
4 EXPECTED_ARGS=2
5 E_BADARGS=65
7 if [ $# -ne $EXPECTED_ARGS ]
8 # 检查命令行参数的个数是否正确.
10 echo "Usage: `basename $0` phone# text-file"
11 exit $E_BADARGS
15 if [ ! -f "$2" ]
17 echo "File $2 is not a text file"
18 exit $E_BADARGS
22 fax make $2 # 从纯文本文件中创建传真格式的文件.
24 for file in $(ls $2.0*) # 连接转换过的文件.
25 # 在变量列表中使用通配符.
27 fil="$fil $file"
30 efax -d /dev/ttyS3 -o1 -t "T$1" $fil # 干活的地方.
33 # S.C. 指出, 通过下边的命令可以省去for循环.
34 # efax -d /dev/ttyS3 -o1 -t "T$1" $2.0*
35 # 但这并不十分具有讲解意义[嘿嘿].
这种结构在循环的开头判断条件是否满足, 如果条件一直满足, 那么就一直循环下去 (返回0作为退出状态码). 与for循环的区别是,
while循环更适合在循环次数未知的情况下使用.
while[condition]
&& command...
与for循环一样, 如果想把do和条件判断放到同一行上的话, 还是需要一个分号.
while[condition] ; do
需要注意一下某种特定的while循环, 比如getopts结构, 好像和这里所介绍的模版有点脱节.
例子 10-14.
简单的while循环
1 #!/bin/bash
4 LIMIT=10
6 while [ "$var0" -lt "$LIMIT" ]
8 echo -n "$var0 " # -n 将会阻止产生新行.
9 # ^& 空格, 数字之间的分隔.
11 var0=`expr $var0 + 1` # var0=$(($var0+1))&
12 # var0=$((var0 + 1)) 也可以.
13 # let "var0 += 1"& 也可以.
14 done # 使用其他的方法也行.
例子 10-15.
另一个while循环
#!/bin/bash
while [ "$var1" != "end" ]
echo "Input variable #1 (end to exit) "
echo "variable #1 = $var1"
一个while循环可以有多个判断条件. 但是只有最后一个才能够决定是否能够退出循环. 然而这里需要一种有点特殊的循环语法.
例子 10-16.
多条件的while循环
1 #!/bin/bash
3 var1=unset
4 previous=$var1
6 while echo "previous-variable = $previous"
previous=$var1
[ "$var1" != end ] # 纪录之前的$var1.
10&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 这个"while"中有4个条件, 但是只有最后一个能够控制循环.
11&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# *最后*的退出状态就是由这最后一个条件来决定.
13 & echo "Input variable #1 (end to exit) "
14 & read var1
15 & echo "variable #1 = $var1"
18 # 尝试理解这个脚本的运行过程.
19 # 这里还是有点小技巧的.
与for循环一样, while循环也可以通过(())来使用C风格的语法. (参考例子 9-31).
例子 10-17.
C风格的while循环
1 #!/bin/bash
2 # wh-loopc.sh: 循环10次的"while"循环.
4 LIMIT=10
7 while [ "$a" -le $LIMIT ]
9 echo -n "$a "
10 let "a+=1"
11 done # 到目前为止都没有什么令人惊奇的地方.
+=================================================================+
17 # 现在, 重复C风格的语法.
19 ((a = 1)) # a=1
20 # 双圆括号允许赋值两边的空格, 就像C语言一样.
22 while (( a &= LIMIT )) # 双圆括号, 变量前边没有"$".
24 echo -n "$a "
25 ((a += 1)) # let "a+=1"
26 # Yes, 看到了吧.
27 # 双圆括号允许像C风格的语法一样增加变量的值.
32 # 现在, C程序员可以在Bash中找到回家的感觉了吧.
这个结构在循环的顶部判断条件, 并且如果条件一直为false, 那么就一直循环下去. (与while循环相反).
until[condition-is-true]
command...
注意, until循环的条件判断在循环的顶部, 这与某些编程语言是不同的.与for循环一样, 如果想把do和条件判断放在同一行里,
那么就需要使用分号.
until[condition-is-true] ; do
#!/bin/bash
END_CONDITION=end
until [ "$var1" = "$END_CONDITION" ]
& echo "Input variable #1 "
& echo "($END_CONDITION to exit)"
& read var1
& echo "variable #1 = $var1"
10.2. 嵌套循环
嵌套循环就是在一个循环中还有一个循环, 内部循环在外部循环体中. 在外部循环的每次执行过程中都会触发内部循环, 直到内部循环执行结束.
外部循环执行了多少次, 内部循环就完成多少次. 当然, 无论是内部循环还是外部循环的break语句都会打断处理过程.
#!/bin/bash
# nested-loop.sh:
for a in 1 2 3 4 5
& echo "Pass $outer in outer loop."
& echo "---------------------"
& for b in 1 2 3 4 5
&&& echo "Pass
$inner in inner loop."
"inner+=1"
& let "outer+=1"
$ bash nested-loop.sh
Pass 1 in outer loop.
---------------------
Pass 1 in inner loop.
Pass 2 in inner loop.
Pass 3 in inner loop.
Pass 4 in inner loop.
Pass 5 in inner loop.
Pass 2 in outer loop.
---------------------
Pass 1 in inner loop.
Pass 2 in inner loop.
Pass 3 in inner loop.
Pass 4 in inner loop.
Pass 5 in inner loop.
Pass 3 in outer loop.
---------------------
Pass 1 in inner loop.
Pass 2 in inner loop.
Pass 3 in inner loop.
Pass 4 in inner loop.
Pass 5 in inner loop.
Pass 4 in outer loop.
---------------------
Pass 1 in inner loop.
Pass 2 in inner loop.
Pass 3 in inner loop.
Pass 4 in inner loop.
Pass 5 in inner loop.
Pass 5 in outer loop.
---------------------
Pass 1 in inner loop.
Pass 2 in inner loop.
Pass 3 in inner loop.
Pass 4 in inner loop.
Pass 5 in inner loop.
10.3. 循环控制
影响循环行为的命令
break, continue
break和continue这两个循环控制命令 [1]与其他语言的类似命令的行为是相同的. break命令用来跳出循环,
而continue命令只会跳过本次循环, 忽略本次循环剩余的代码, 进入循环的下一次迭代.
例子 10-20.
break和continue命令在循环中的效果
#!/bin/bash
echo "Printing Numbers 1 through 20 (but not 3 and 11)."
while [ $a -le "$LIMIT" ]
a=$(($a+1))
if [ "$a" -eq 3 ] || [ "$a" -eq 11 ]
echo -n "$a "
echo Printing Numbers 1 through 20, but something happens after
while [ "$a" -le "$LIMIT" ]
a=$(($a+1))
if [ "$a" -gt 2 ]
echo -n "$a "
$ bash break_test.sh
Printing Numbers 1 through 20 (but not 3 and 11).
1 2 4 5 6 7 8 9 10 12 13 14 15 16 17 18 19 20
Printing Numbers 1 through 20, but something happens after 2.
break命令可以带一个参数. 一个不带参数的break命令只能退出最内层的循环, 而break N可以退出N层循环.
例子 10-21. 多层循环的退出
$ cat break-levels.sh
#!/bin/bash
# break-levels.sh
for outerloop in 1 2 3 4 5
& echo -n "Group $outerloop: "
& for innerloop in 1 2 3 4 5
&&& echo -n
"innerloop=$innerloop "
"$innerloop" -eq 3 ]
##break最内层循环的输出结果
$ bash break-levels.sh
Group 1: innerloop=1 innerloop=2 innerloop=3
Group 2: innerloop=1 innerloop=2 innerloop=3
Group 3: innerloop=1 innerloop=2 innerloop=3
Group 4: innerloop=1 innerloop=2 innerloop=3
Group 5: innerloop=1 innerloop=2 innerloop=3
##break 2 第二层循环的输出结果
$ bash break-levels.sh
Group 1: innerloop=1 innerloop=2 innerloop=3
continue命令也可以象break命令一样带一个参数. 一个不带参数的continue命令只会去掉本次循
环的剩余代码. 而continue N将会把N层循环的剩余代码都去掉, 但是循环的次数不变.
例子 10-22.
多层循环的continue
#!/bin/bash
# "continue N"
for outer in I II III IV V
& echo -n "Group $outer: "
& for inner in 1 2 3 4 5 6 7 8 9 10
"$inner" -eq 7 ]
continue 2
&&& echo -n
continue-test.sh
Group I: 1 2 3 4 5 6
Group II: 1 2 3 4 5 6
Group III: 1 2 3 4 5 6
Group IV: 1 2 3 4 5 6
Group V: 1 2 3 4 5 6
$ bash continue-test.sh
Group I: 1 2 3 4 5 6 8 9 10
Group II: 1 2 3 4 5 6 8 9 10
Group III: 1 2 3 4 5 6 8 9 10
Group IV: 1 2 3 4 5 6 8 9 10
Group V: 1 2 3 4 5 6 8 9 10
10.4. 测试与分支(case与select结构)
case和select结构在技术上说并不是循环, 因为它们并不对可执行代码块进行迭代. 但是和循环相似的是,
它们也依靠在代码块顶部或底部的条件判断来决定程序的分支.
在代码块中控制程序分支
case (in) / esac
在shell中的case结构与C/C++中的switch结构是相同的. 它允许通过判断来选择代码块中多条路径中的一条.
它的作用和多个if/then/else语句的作用相同, 是它们的简化结构, 特别适用于创建菜单.
&& case"$variable" in
$condition1" )
TT CLASS="REPLACEABLE" &command...
$condition2" )
TT CLASS="REPLACEABLE" &command...
对变量使用""并不是强制的,
因为不会发生单词分割.
每句测试行, 都以右小括号)来结尾.
每个条件判断语句块都以一对分号结尾
case块以esac(case的反向拼写)结尾.
10-24. 使用case
1 #!/bin/bash
2 # 测试字符串范围.
4 echo "Hit a key, then hit return."
5 read Keypress
7 case "$Keypress" in
8 & & [[:lower:]] ) echo
"Lowercase letter";;
9 & & [[:upper:]] ) echo
"Uppercase letter";;
10&& [0-9] ) echo "Digit";;
11&& * ) echo "Punctuation,
whitespace, or other";;
#& 允许字符串的范围出现在[中括号]中,
13&&&&&&&&&&&&
#+ 或者出现在POSIX风格的[[双中括号中.
15 #& 在这个例子的第一个版本中,
16 #+ 测试大写和小写字符串的工作使用的是
17 #+ [a-z] 和[A-Z].
18 #& 这种用法在某些特定场合的或某些Linux发行版中不能够正常工作.
19 # POSIX 的风格更具可移植性.
20 #& 感谢Frank Wang指出了这点.
22 #& 练习:
23 # -----24 #& 就像这个脚本所表现出来的, 它只允许单次的按键,
然后就结束了.
25 #& 修改这个脚本, 让它能够接受重复输入,
26 #+ 报告每次按键, 并且只有在"X"被键入时才结束.
27 #& 暗示: 将这些代码都用"while"循环圈起来.
例子 10-25. 使用case来创建菜单
#!/bin/bash
echo " Contact List"
echo " ------- ----"
echo "Choose one of the following persons:"
echo "[E]vans, Roland"
echo "[J]ones, Mildred"
echo "[S]mith, Julie"
echo "[Z]ane, Morris"
read person
case "$person" in
"E" | "e" )
echo "Roland Evans"
echo "4321 Floppy Dr."
echo "Hardscrabble, CO 80753"
echo "(303) 734-9874"
echo "(303) 734-9892 fax"
echo "Business partner & old friend"
"J" | "j" )
echo "Mildred Jones"
echo "249 E. 7th St., Apt. 19"
echo "New York, NY 10009"
echo "(212) 533-2814"
echo "(212) 533-9972 fax"
echo "Ex-girlfriend"
echo "Birthday: Feb. 11"
echo "Not yet in database."
一个case的非常聪明的用法,
用来测试命令行参数.
$ cat case_test.sh
#! /bin/bash
case "$1" in
"") echo "Usage: ${0##*/} "; exit $E_PARAM;;
-*) FILENAME=./$1;;
* ) FILENAME=$1;;
这是一个命令行参数处理的更容易理解的例子:
1 #! /bin/bash
4 while [ $# -gt 0 ]; do # 直到你用完所有的参数. . .
5 case "$1" in
6 -d|--debug)
7&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 是"-d" 或"--debug" 参数?
10 -c|--conf)
11 CONFFILE="$2"
13 if [ ! -f $CONFFILE ]; then
14 echo "Error: Supplied file doesn't exist!"
$E_CONFFILE&&&&&&&&&
# 错误: 文件未发现.
shift&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 检查剩余的参数.
22 #& 来自Stefano Falsetto的"Log2Rot" 脚本,
23 #+ 并且是他的"rottlog"包的一部分.
24 #& 已得到使用许可.
例子 10-27. 简单的字符串匹配
#!/bin/bash
match_string (){
& NOMATCH=90
& PARAMS=2
& BAD_PARAMS=91
& [ $# -eq $PARAMS ] || return $BAD_PARAMS
& case "$1" in
&&& "$2") return
&&& * ) return
$NOMATCH;;
match_string $a
match_string $a $b
match_string $b $d
例子 10-28. 检查输入字符是否为字母
#!/bin/bash
# isalpha.sh:
FAILURE=-1
isalpha (){
& if [ -z "$1" ]
&&& return
& case "$1" in
&&& [a-zA-Z]*)
return $SUCCESS;;
&&& * ) return
$FAILURE;;
isalpha2 (){
& [ $# -eq 1 ] || return $FAILURE
& case $1 in
*[!a-zA-Z]*|"") return $FAILURE;;
&&& *) return
$SUCCESS;;
isdigit (){
& [ $# -eq 1 ] || return $FAILURE
& case $1 in
&&& *[!0-9]*|"")
return $FAILURE;;
&&& *) return
$SUCCESS;;
check_var (){
& if isalpha "$@"
&&& echo "\"$*\"
begins with an alpha character."
&&& if isalpha2
echo "\"$*\" contains only alpha characters."
echo "\"$*\" contains at least one non-alpha character."
&&& echo "\"$*\"
begins with a non-alpha character."
digit_check (){
& if isdigit "$@"
&&& echo "\"$*\"
contains only digits [0 - 9]."
&&& echo "\"$*\"
has at least one non-digit character."
a=23skidoo
e=`echo $b`
check_var $a
check_var $b
check_var $c
check_var $d
check_var $e
check_var $f
digit_check $g
digit_check $h
digit_check $i
select结构是建立菜单的另一种工具, 这种结构是从ksh中引入的.
select variable[in list]
TT CLASS="REPLACEABLE" &command...
提示用户输入选择的内容(比如放在变量列表中). 注意: select命令使用PS3提示符, 默认为
(#?), 当然, 这可以修改.
例子 10-29.
使用select来创建菜单
$ cat select_test.sh
#!/bin/bash
PS3='Choose your favorite vegetable: '
select vegetable in "beans" "carrots" "potatoes" "onions"
"rutabagas"
&& echo "Your favorite veggie is
$vegetable."
&& echo "Yuck!"
例子 10-30.
使用函数中的select结构来创建菜单
#!/bin/bash
PS3='Choose your favorite vegetable: '
choice_of(){
& select vegetable
&&& echo "Your
favorite veggie is $vegetable."
choice_of beans rice carrots radishes tomatoes spinach
$ bash select_test1.sh
3) carrots
4) radishes
5) tomatoes
6) spinach
ASCII(American Standard Code for
Information
Interchange,美国信息互换标准代码,ASCⅡ)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语和其他西欧语言。它是现今最通用的
单字节编码系统,并等同于国际标准ISO/IEC 646。
  ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符,其中33个字符无法显示
(这是以现今操作系统为依归,但在DOS模式下可显示出一些诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符,控制字符
的用途主要是用来操控已经处理过的文字,在33个字符之外的是95个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空
ASCII控制字符
可以显示的表示法
空字符(Null)
水平定位符号
垂直定位符号
取消变换(Shift out)
启用变换(Shift in)
跳出数据通讯
设备控制一(XON 启用软件速度控制)
设备控制二
设备控制三(XOFF 停用软件速度控制)
设备控制四
确认失败回应
同步用暂停
区块传输结束
连接介质中断
文件分割符
组群分隔符
记录分隔符
单元分隔符
ASCII可显示字符
(空格)(␠)
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 ack互助理财 的文章

 

随机推荐