在测试程序当中,“让一部分事情自动去做”往往是绝对优先的选择,通常最新的菜鸟也要选择脚本语言,学着“让它自动去做”来作为第一项基本功,本篇不属于脚本高手的参考,而是旨在指导菜鸟如何用三天时间学会快速编程。
本篇文章包括脚本语言用到的较全的概念,而且这也是菜鸟写出脚本的最基本条件,累赘少许我们开始吧。
一. 正则表达
处理杂乱无章的字符文件数据,如何获得有效信息,就要使用特定的过滤办法,正则表达的概念正用于此,它集成一系列特殊符号附加特定的pattern办法来实现强大的过滤功能,这里很值得玩味。
1. 特殊符号表:
^ 锚定行的开始 如:'^grep'匹配所有以grep开头的行。
$ 锚定行的结束 如:'grep$'匹配所有以grep结尾的行。
. 匹配一个非换行符的字符 如:'gr.p'匹配gr后接一个任意字符,然后是p。
* 匹配零个或多个先前字符 如:'*grep'匹配所有一个或多个空格后紧跟grep的行。 .*一起用代表任意字符。
[] 匹配一个指定范围内的字符,如'[Gg]rep'匹配Grep和grep。
[^] 匹配一个不在指定范围内的字符,如:'[^A-FH-Z]rep'匹配不包含A-R和T-Z的一个字母开头,紧跟rep的行。
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
\w 匹配文字和数字字符,也就是[A-Za-z0-9],如:'G\w*p'匹配以G后跟零个或多个文字或数字字符,然后是p。
\d任意一个数字,0~9 中的任意一个
\s包括空格、制表符、换页符等空白字符的其中任意一个
\b匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符
\W 匹配任意不是字母和数字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
| 左右两边表达式之间 "或" 关系,匹配左边或者右边
( ) 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰;取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到
反向引用 \1, \2, ... 通常结合()来使用的,整体代表参数,属于贪婪法则。
2. 注意事项:
ab^bc和ab$bc当中的^和$都失去了作开头和结尾公用的目的,相当于直接使用^和$来使用,等同于加转义字符ab\^bc和ab\$bc
[^]单独这样使用没有意义。
[.*]里面的点和美元符号并不表示任何字符匹配任何次数,而是直接指.和*,如果其中加入[\.\*]来对他做转义反而会报错,这是因为[]表示选择当中的一个字符作为可能过滤。
3. 实例和工具:
网络上的实例到处都是,直接看搜一篇就搞定了,关键是要记住。推荐一个工具Regex Match Tracer.
网址http://www.regexlab.com/zh/regref.htm
二. grep 锦囊
grep是linux脚本语言里头最常用的一个命令,先掌握它的基本用法吧,我们可以man grep,但只要理解和活用当中的几项就可以大显身手了。
1. man grep
-c 只输出匹配行的个数。
-i 不区分大小写(只适用于单字符)。
-h 查询多文件时不显示文件名。
-l 查询多文件时只输出包含匹配字符的文件名。
-n 显示匹配行及行号。
-s 不显示不存在或无匹配文本的错误信息。
-v 显示不包含匹配文本的所有行。
-V 显示软件版本信息
2. grep 正则
grep结合正则表达才叫真,注意要用引号给它括起来,格式如下:
grep -option "pattern" source-file(s)
grep的正则表达有点不同于上面的阐述,其实也是由于BRE和ERE的正则标准。grep一般使用BRE(Baisc Regular Express)标准.ERE是Extend.
几点不同列表如下,即多加了几道反斜杠来解释meta字符:
\(..\) 标记匹配字符,如'\(love\)',love被标记为1。
\> 锚定单词的结束,如'grep\>'匹配包含以grep结尾的单词的行。
x\{m\} 重复字符x,m次,如:'0\{5\}'匹配包含5个o的行。
x\{m,\} 重复字符x,至少m次,如:'o\{5,\}'匹配至少有5个o的行。
x\{m,n\}重复字符x,至少m次,不多于n次,如:'o\{5,10\}'匹配5--10个o的行。
三. sed 匕首
sed 的功能比grep更加强大,它主要体现在对文件的搜索,替换,搬移,删除功能,面向的目标是文件的行,执行的时候是一行一行的读取文件再进行过滤处理,据此我从这四个部分进行讲解。
这四种状态包含sed的各种书写格式,这是容易混淆的地方,但是确实指示清晰理的地方。下文中pattern表示要使用的正则表达,address[X]表示文本的位置,replacement表示要去取代的源串,action表示要进行的动作,file(s)表示即将处理里的目标文件。
1. sed的命令参数
-f表示用文件的形式编写sed命令,sed -f sedscript即可。
-e表示编辑文本,后面根据pattern或者replacement或者action处理文件。
-n表示silent模式,如果要在终端打印出信息,必须明确制定p参数
2. sed的搜索功能
1) 格式:sed -e "/pattern/" files
2) 这种搜索功能同grep大同小异,无非它在前面添加了一个斜杠尾部添加了一个斜杠表示分界。
3. sed的替换功能
1) 格式: sed -e "s/pattern/replacement/[action]" files
replacement 是要替换经pattern正则表达后的文本。
2) 当action为g时,代表替换所有符合(match)的字串。
当action为十进位数字m时,代表替换行内第m个符合的字串。
当action为p时,代表替换第一个符合pattern的字串後,将资料输出标准输出档。
当action为w wfile时,代表替换第一个符合pattern的字串後,输出到wfile档内(如果wfile不存在,则会重新开启名为wfile的档案)。
当没有action时,则将资料行内第一个符合pattern的字串以replacement字串来替换。
4. sed的删除功能
格式: sed -e "address1,addres2[action]" files
sed -e "/pattern/d" files
sed -e "/pattern1/,/pattern2/[action]" files
一三项正是都好起作用,表示从哪里行到哪里行,起匹配的哪里到哪里。
5. sed的搬迁功能
这里要理解两个概念:pattern space 和 hold space.
pattern space 表示sed读取一行到内存缓存中,经sed的正则处理之后,再把结果送出到终端输出。
hold space 表示在patternspace里面的内容需要再进行一次处理,这时候就要把结果(类似中间状态)放到一个空间再做下一步处理,这个中间状态的空间就是holdspace,例如两个处理结果分别得到,但两者结构又需要用对方的数据,这时候就要把一项放到holdspace来进行保存呢
1) 格式: sed -e "address1, address2 [action]"
sed -e "/pattern/[action] file" files
action 为下列列表
2) 列表:
!不执行函数参数。
=印出资料行数( line number )。
a添加使用者输入的资料。
b label将执行的指令跳至由:建立的参考位置。
c以使用者输入的资料取代资料。
d删除资料。
D删除pattern space内第一个newline字母前的资料。
g拷贝资料从hold space。
G添加资料从hold space至pattern space。
h拷贝资料从pattern space至hold space。
H添加资料从pattern space至hold space。
l印出l资料中的nonprinting character用ASCII码。
i插入添加使用者输入的资料行。
n读入下一笔资料。
N添加下一笔资料到pattern space。
p印出资料。
P印出pattern space内第一个newline字母前的资料。
q跳出sed编辑。
r读入它档内容。
s替换字串。
t label先执行一替换的编辑指令,如果替换成牛p>则将编辑指令跳至: label处执行。
w写资料到它档内。
x交换hold space与pattern space内容。
y转换(transform)字元。
6. sed 的注意事项。
1) sed -e "s/test/& me/g" file 表示用test me 代替test, &表示的是pattern字符串(不是特殊符号)
2) 回想反向引用\1 \2. sed的参数规则是如下的:sed -e "/(test) (me) (again)/[2 3 1]/g"
,它用的是方括号添上数字表示。这句话表示用me again test 代替test me again.
3) 待工作中补充。
四. awk 大刀
对字符过滤的又一大补充,正是awk这个砍刀,它不像grep仅仅面向查找行和sed面向编辑行,它是续grep和sed处理之后对字段进行处理的过滤工具。
awk当中融入正则表达,awk编程语法(类似C加少数shell,变量定义不需要初始值),内置函数处理与内置变量。
调用awk:
第一种,命令行方式,如:awk [-F field-separator] ‘commands’ input-file(s)这里commands是真正的awk命令,[-F域分隔符]是可选的,awk默认使用空格分隔,因此如果要浏览域间有空格的文本,不必指定这个选项,但如 果浏览如passwd文件,此文件各域使用冒号作为分隔符,则必须使用-F选项: awk -F : ‘commands’ input-file;第二种,将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它第三种,将所有awk命令插入一个单独文件,然后调用: awk -f awk-script-file input-file -f选项指明在文件awk-script-file的awk脚本,input-file是使用awk进行浏览的文件名
awk脚本:
awk脚本由各种操作和模式组成,根据分隔符(-F选项),默认为空格,读取的内容依次放置到对应的域中,一行一行记录读取,直到文件尾模式和动作:任何awk语句都是由模式和动作组成,在一个awk脚本中可能有许多语句。模式部分决定动作语句何时触发及触发事件。动作即对数据进行的操作,如果省去模式部分,动作将时刻保持执行状态模式可以是任何条件语句或复合语句或正则表达式,模式包含两个特殊字段BEGIN和END,使用BEGIN语句设置计数和打印头,BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行;END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态标志,有动作必须使用{}括起来,实际动作在大括号{}内指明,常用来做打印动作,但是还有更长的代码如if和循环looping语句及循环退出等,如果不指明采取什么动作,awk默认打印出所有浏览出的记录
域和记录:
awk执行时,其浏览标记为$1,$2…$n,这种方法称为域标记.使用$1,$3表示参照第1和第3域,注意这里使用逗号分隔域,使用$0表示使用所有域例:awk ‘{print $0}’ temp.txt > sav.txt 表示打印所有域并把结果重定向到sav.txt中awk ‘{print $0}’ temp.txt|tee sav.txt 和上例相似,不同的是将在屏幕上显示出来
awk ‘{print $1,$4}’ temp.txt 只打印出第1和第4域awk ‘BEGIN {print “NAME GRADE\n————-”} {print $1″\t”$4}’ temp.txt表示打信息头,即输入的内容的第一行前加上”NAME GRADE\n————-”,同时内容以tab分开
awk ‘BEGIN {print “being”} {print $1} END {print “end”}’ temp 同时打印信息头和信息尾
条件操作符:
<、<=、==、!=、>=、~匹配正则表达式、!~不匹配正则表达式
匹配:awk ‘{if ($4~/ASIMA/) print $0}’ temp 表示如果第四个域包含ASIMA,就打印整条
awk ‘$0 ~ /ASIMA/’ temp 表示只要整条包含ASIMA就打印出来
精确匹配:awk ‘$3==”48″ {print $0}’ temp 只打印第3域等于”48″的记录
不匹配: awk ‘$0 !~ /ASIMA/’ temp 打印整条不包含ASIMA的记录
不等于: awk ‘$1 != “asima”‘ temp
小于: awk ‘{if ($1<$2) print $1 “is smaller”}’ temp
设置大小写: awk ‘/[Gg]reen/’ temp 打印整条包含Green,或者green的记录
任意字符: awk ‘$1 ~/^…a/’ temp 打印第1域中第四个字符是a的记录,^行首,.任意字符或关系匹配: awk ‘$0~/(abc)|(efg)/’ temp 使用|时,语句需要括起来AND与关系: awk ‘{if ( $1==”a” && $2==”b” ) print $0}’ temp
OR或关系: awk ‘{if ($1==”a” || $1==”b”) print $0}’ temp
awk内置变量:
ARGC 命令行参数个数 AGRV 命令行参数排列 ENVIRON 支持队列中系统环境变量的使用FILENAME awk浏览的文件名 FNR 浏览文件的记录数 FS 设置输入域分隔符,同- F选项NF 浏览记录的域个数 NR 已读的记录数 OFS 输出域分隔符
ORS 输出记录分隔符 RS 控制记录分隔符例: awk ‘END {print NR}’ temp 在最后打印已读记录条数awk ‘{print NF,NR,$0} END {print FILENAME}’ temp
awk ‘{if (NR>0 && $4~/Brown/) print $0}’ temp 至少存在一条记录且包含Brown
NF的另一用法: echo $PWD | awk -F/ ‘{print $NF}’ 显示当前目录名
awk操作符:
在awk中使用操作符,基本表达式可以划分成数字型、字符串型、变量型、域及数组元素
设置输入域到变量名:
awk ‘{name=$1;six=$3; if (six==”man”) print name ” is ” six}’ temp
域值比较操作:
awk ‘BEGIN {BASE=”27″} {if ($4<BASE) print $0}’ temp
修改数值域取值 :( 原输入文件不会被改变)
awk ‘{if ($1==”asima”) $6=$6-1;print $1,$6,$7}’ temp
修改文本域:
awk ‘{if ($1==”asima) ($1==”desc”);print $1}’ temp
只显示修改记录 :( 只显示所需要的,区别上一条命令,注意{})
awk ‘{if ($1==”asima) {$1==”desc”;print$1}}’ temp
创建新的输出域:
awk ‘{$4=$3-$2; print $4}’ temp
统计列值:
awk ‘(tot+=$3);END {print tot}’ temp 会显示每列的内容
awk ‘{(tot+=$3)};END {print tot}’ temp 只显示最后的结果
文件长度相加:
ls -l|awk ‘/^[^d]/ {print $9″\t”$5} {tot+=$5} END{print “totKB:” tot}’
只列出文件名:
ls -l|awk ‘{print $9}’ 常规情况文件名是第9域
awk内置字符串函数:
gsub(r,s) 在整个$0中用s替代r
awk ‘gsub(/name/,”xingming”) {print $0}’ temp
gsub(r,s,t) 在整个t中用s替代r
index(s,t) 返回s中字符串t的第一位置
awk ‘BEGIN {print index(“Sunny”,”ny”)}’ temp 返回4
length(s) 返回s的长度
match(s,r) 测试s是否包含匹配r的字符串
awk ‘$1==”J.Lulu” {print match($1,”u”)}’ temp 返回4
split(s,a,fs) 在fs上将s分成序列a
awk ‘BEGIN {print split(“12#345#6789″,myarray,”#”)”‘
返回3,同时myarray[1]=”12″, myarray[2]=”345″, myarray[3]=”6789″
sprint(fmt,exp) 返回经fmt格式化后的exp
sub(r,s) 从$0中最左边最长的子串中用s代替r(只更换第一遇到的匹配字符串)
substr(s,p) 返回字符串s中从p开始的后缀部分
substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分
printf函数的使用:
字符转换: echo “65″ |awk ‘{printf “%c\n”,$0}’ 输出A
awk ‘BEGIN {printf “%f\n”,999}’ 输出999.000000
格式化输出:awk ‘{printf “%-15s %s\n”,$1,$3}’ temp 将第一个域全部左对齐显示
其他awk用法:
向一行awk命令传值:
awk ‘{if ($5<AGE) print $0}’ AGE=10 temp
who | awk ‘{if ($1==user) print $1 ” are in ” $2 ‘ user=$LOGNAME 使用环境变量
awk数组:
awk的循环基本结构 For (element in array) print array[element]
awk ‘BEGIN {record=”123#456#789″;split(record,myarray,”#”)}
END { for (i in myarray) {print myarray[i]} }
五. Bash语法
所有脚本语言的语法都是简单容易的,稍稍用点功夫就可以完全掌握的但也是非常容易忘记的,我建议从以下几点记住并活用便可以很好的用在开发当中,现在逐一列出并讲述。
1. 符号对变量的定义
1) 基本定义:
${var}用{}来定义一个变量,若变量不长,可直接$var,若$var的var后面跟了字符,则一定要用{}括起来表示是对之前已定义的变量取值。
若var是数字,则表示是脚本输入的参数,如果数字大于10,必须要用{}括起来。
dollor$后面如果跟{}表示取变量,如果是跟()表示取()里面语句(注意是语句)执行结果的值为变量。
2) 定义预先处理:
${var:-word}表示var若没有被定义NULL,则使用word初始化左值,注意左值不是var而是另一个变量
例: sh=${var:-licheng} 表示若var空$sh=licheng
${var:+word}表示va若没有定义或者为NULL,则返回NULL,否则返回word,这个处理主要是做var是否曾被定义
${var:=word}类同于${var:-word}
3) 定义变量截断:
优先考虑的linux命令:dirname and basename
其次${var##.*} 或者${var#.*},这里的.和*不是正则表达式里面的概念,而是DOS系统匹配的概念,.没有什么意义表示点号,*表示匹配任何个字符,当然可以想象?表示匹配任何一个字符。这里的"##","#"表示截断离前面(左边)最远的匹配子字符串和最近的匹配子字符串,保留后面(右边)的字符串做变量引用。由此可知,截断的概念也类似于从字符串中取字符串做新变量定义声明。
最后有类似的${var%%.*}或者${var%.*}表示截断离后面(右边)最远的匹配子字符串和最近的匹配子字符串,保前面(左边)的字符串做变量引用
4) 参数的符号表示:
$# 表示所有参数的个数,常用shift命令来进行遍历。
$? 表示脚本语句运行时候的状态,测试运行的结果正确还是错误。
$* 表示脚本所有参数,主要要关注与"$@"的区别
$@ 表示脚本所有参数,主要要关注与"$*"的区别
"$*" 表示所有的参数当做一个字符串来进行处理,如两个参数$1=var1 $2=var2,则"$*"为"var1 var2"
"$@" 任然表示所有单独的个体参数,常给for遍历用。如两个参数$1=var1 $2=var2,则"$@"为"var1" "var2"
2. 基本语法
1) test 和 [] 测试条件命令。常用-x,-f,-z等,可以查阅网上书籍。
2) if []; then
elif []; then
fi
3) case var in
p1)
… ;;
p2)
… ;;
*)
… ;;
esac 注意case没有像C语言当中的default项。
4) while [ cond1 ] && { || } [ cond2 ] …; do
…
done
5) for var in …; do
…
done
for (( cond1; cond2; cond3 )) do
…
done
6) until [ cond1 ] && { || } [ cond2 ] …; do
…
done
3. 数组应用(待补充)
4. 函数应用
1) ./path引入,类似于C的include包含定义和函数声明。
2) 函数参数由$1,$2,$3使用,最好不要过多,若多分成更小模块编写。
5. shell编程常用到的linux处理命令和注意事项(不断更新中):
1) eval:eval 应用来源:[root@localhost] a="id | cut -f 4 -d ' ' " $a出错,执行eval $a正确,也就是说,直接对命令赋值成变量来执行会导致被赋值的命令在参数解析上出错.
2) local:loacl表示定义局部变量,由于函数经常定义在最前头,在用的时候有些变量会重用,在函数中使用local会防止修改函数前后同名的变量值,从而有效地在函数内部进行计算而不会影响前后变量。
3) declare and let: declare 可以声明一个即将可以做何种类型计算的变量声明,常用在数学计算,也用在数组定义,它有几个参数是应该熟悉的:
declare –a name :表示数组array。
declare –f name :表示是function的名字。
declare –F name :同上,但只显示function的名字。这个和上面的具体差异不太明白,但是这两者都 很少使用,先不理会它们。
declare –i name :表示整数
declare –r name :表示只读。不能使用unset。对于只读变量,也可以使用readonly name 的方式,相当于declare –r name 。readonly可以带三个选项:-f表示这是个function的名字,-p表示打印所有的readonly的名字,-a表示这是个只读的数组。
declare –x name :同export,即不仅在当前的环境中起作用,也在外部的shell环境中起作用。
let表示让一个变量进行数学运算。
实例
[root@localhost ~]# a=2;echo $a; let a=a-1; echo $a
[root@localhost ~]# declare -i a=2;echo $a; a=a-1; echo $a
6.shell字符串的截取的问题:
6.1、Linux shell 截取字符变量的前8位,有方法如下:1.expr substr “$a” 1 8
2.echo $a|awk ‘{print substr(,1,8)}’
3.echo $a|cut -c1-8
4.echo $
5.expr $a : ‘\(.\\).*’
6.echo $a|dd bs=1 count=8 2>/dev/null
6.2、按指定的字符串截取
1、第一种方法:
${varible##*string} 从左向右截取最后一个string后的字符串
${varible#*string}从左向右截取第一个string后的字符串
${varible%%string*}从右向左截取最后一个string后的字符串
${varible%string*}从右向左截取第一个string后的字符串
“*”只是一个通配符可以不要
例子:
$ MYVAR=foodforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg
2、第二种方法:${varible:n1:n2}:截取变量varible从n1到n2之间的字符串。
可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。试着在 bash 中输入以下行:
$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga
这种形式的字符串截断非常简便,只需用冒号分开来指定起始字符和子字符串长度。
6.3、按照指定要求分割:
比如获取后缀名
ls -al | cut -d “.” -f2
7. 各种炸碎补充:
1) $((_SLR&0x0))实现的操作时样_SLR变量能够进行与操作,修改_SLR的原始值。