导读:
1,awk基本语法
2,比较操作符
3,复合表达式
4,next命令
5,变量及变量赋值
6,awk中的赋值操作符
7,BEGIN和END
8,内置变量
9,awk使用shell变量
10,awk中的三种流控制语句
10.1 if语句
10.2 while语句:
10.3 for语句:
11,实验:
12,shell里的特殊变量
正文:
awk ‘script’ files
script 由下面的结构组成 /pattern/{actions}
例子:打印fruit_prices.txt文件的内容
[root@master bkeep]# awk '{print;}' fruit_prices.txt
fruit price/lbs quantity
banana $0.89 100
peach $0.79 65
kiwi $1.50 22
pineapple $1.29 35
apple $0.99 78
TIPS:留意printf的用法 如果加“-”说明是左对齐!
[root@master bkeep]# awk '{printf "%-15s %s\n", $1, $3;}' fruit_prices.txt
fruit quantity
banana 100
peach 65
kiwi 22
pineapple 35
apple 78
留意printf的用法 如果不加“-”说明是右对齐!
[root@master bkeep]# awk '{printf "%15s %s\n", $1, $3;}' fruit_prices.txt
fruit quantity
banana 100
peach 65
kiwi 22
pineapple 35
apple 78
例子:给价格高于1美元的水果加个星号
[root@master bkeep]# cat fruit_prices.txt
fruit price/lbs quantity
banana $0.89 100
peach $0.79 65
kiwi $1.50 22
pineapple $1.29 35
apple $0.99 78
[root@master bkeep]# cat price.sh
#!/bin/bash
awk '
/ *\$[1-9][0-9]*\.[0-9][0-9] */ {print $1,$2,$3,"*";} //星号前面一定要留空格,否则匹配不到
/ *\$0\.[0-9][0-9] */ {print;} //以后养成习惯,/ key /斜杠和key之间始终留空格
' fruit_prices.txt
执行结果:
[root@master bkeep]# ./price.sh
banana $0.89 100
peach $0.79 65
kiwi $1.50 22 *
pineapple $1.29 35 *
apple $0.99 78
上面的格式很不好看,我们改改它吧!
TIPS:$0:awk使用变量0原原本本地存储它所读入的整个输入行。这样可以保持和原来文本格式相同的输出
[root@master bkeep]# cat price.sh
#!/bin/bash
awk '
/ *\$[1-9][0-9]*\.[0-9][0-9] */ {print $0,"*";}
/ *\$0\.[0-9][0-9] */ {print;}
' fruit_prices.txt
[root@master bkeep]# ./price.sh
banana $0.89 100
peach $0.79 65
kiwi $1.50 22 *
pineapple $1.29 35 *
apple $0.99 78
语法:expression {actions;}
expression 可以使用以下操作符构成
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于
!= 不等于
value ~ /pattern/ 若value匹配pattern则为真
value !~ /pattern/ 若value不匹配pattern则为真
说明:$2 ~ /^\$[1-9][0-9]* 将匹配第二列以$开头,然后大于1的二位数。(注意,这里是1而不是10)
例子:给数量大于50的水果加星号
[root@master bkeep]# cat larger.sh
#!/bin/bash
awk '
$3>50 {printf "%s\t%s\n", $0,"*"}
$3<=50 {printf "%s\n",$0}
' fruit_prices.txt
[root@master bkeep]# ./larger.sh
fruit price/lbs quantity *
banana $0.89 100 *
peach $0.79 65 *
kiwi $1.50 22
pineapple $1.29 35
apple $0.99 78 *
语法:(compound expression)
(expr1)&& (expr2) ------- 与
(expr1)|| (expr2) ------- 或
说明一下:符合表达式必须用括号括起来
当使用&&时,expr1和expr2必须同时为零(零代表真!呵呵~),复合表达式才为真;
当使用 || 时,expr1和expr2 只要有一个为真,复合表达式则为真!
例子:显示价格高于1美元且数量少于50的水果记录,给打个标记!
[root@master bkeep]# cat larger.sh
#!/bin/bash
awk '
($2 ~ /^\$[1-9][0-9]*\.[0-9][0-9]$/) && ($3<50) { //这是一个模式匹配(请参阅比较操作符
printf "%s\t%s\t%s\n", $0,"*","reorder";
}
' fruit_prices.txt
[root@master bkeep]# ./larger.sh
kiwi $1.50 22 * reorder
pineapple $1.29 35 * reorder
例子:给库存少于50的水果价格标签!
[root@master bkeep]# cat next.sh
#!/bin/bash
awk '
$3 <= 50 { printf "%s\t%s\n",$0,"REORDER" ; }
$3 > 50 {print $0; }
' fruit_prices.txt
当输入行为:kiwi $1.50 22
脚本执行以下过程:
1)检查是否第三列的值22小于50,由于值小于50,执行脚本的第二步
2)打印该行,并在行尾添加REORDER标志
3)检查是否第三列的值22大于50,由于值不大于50,脚本读下一行
呵呵,傻子都看得出这种情况下执行第三步是多余的。那么如何跳过呢?可以使用next命令。
next命令:告诉awk跳过剩下的所有模式和表达式,并读取输入的下一行,同时从第一个模式或表达式开始处理该行!
修改后的脚本,加入了next命令!
[root@master bkeep]# cat next.sh
#!/bin/bash
awk '
$3 <= 50 { printf "%s\t%s\n",$0,"REORDER" ;next; }
$3 > 50 {print $0; }
' fruit_prices.txt
[root@master bkeep]# ./next.sh
fruit price/lbs quantity
banana $0.89 100
peach $0.79 65
kiwi $1.50 22 REORDER
pineapple $1.29 35 REORDER
apple $0.99 78
现在脚本执行如下;
1)检查是否第三列的值22小于50,由于值小于50,所以执行脚本第二步
2)打印输出行并在该行后打印REORDER标志(有了next后就不再执行$3 > 50 {print $0; })
3)读取下一输入行并从第一种模式开始
下面给变量赋值
fruit=100 //把数字赋给fruit变量
fruit=”peach” //把字符串赋给fruit变量
fruit=$1 //把第一列赋给fruit变量
awk中的数字操作符:
+ 加
- 减
* 乘
/ 除
% 求余
^ 求幂
应用例子:统计文件中的空行数
[root@master bkeep]# cat for.sh
#!/bin/bash
for i in $@; //这里我们不知道有多少个文件需要统计,所以用$@来接收参数!
do
if [ -f $i ]; then
echo $i
awk ' /^$/ { x=x+1;} END{print x;}' $i
else
echo "ERROR:$i not a file." >&2
fi
done
[root@master bkeep]# ./for.sh aa bb
aa //文件名为aa
7 //空行数7
bb //文件名为bb
4 //空行数为4
+= x+=1 就是x=x+1
-= x-=1 就是x=x-1
*= x*=1 就是x=x*1
/= x/=1 就是x=x/1
%= x%=2 就是x=x%2
^= x^=2 就是x=x^2
awk的语法:/pattern/ {action} pattern:模式
这里pattern有两个特殊模式BEGIN和END
下面是BEGIN END的语法:
awk ‘
BEGIN { actions }
/ pattern / { actions }
/ pattern / { actions }
END { actions }
‘ files
BEGIN和END都是可选的
·当指定BEGIN模式时,awk在读取任何输入前执行动作 actions
·当指定END模式时,awk在其退出前执行动作actions
这里要特别注意:比如awk读入aa.txt。那么BEGIN只在读入aa时执行一次,然后按行处理的时候不再执行BEGIN指定的action!(也可以这么理解:BEGIN只对文件生效,而不是按行生效!!);理解了BEGIN,END还会糊涂吗?呵呵!
gawk语言中有几个十分有用的内置变量,现在列于下面:
NR 已经读取过的记录数。
FNR 从当前文件中读出的记录数。
FILENAME 输入文件的名字。
FS 字段分隔符(缺省为空格)。
RS 记录分隔符(缺省为换行)。
OFMT 数字的输出格式(缺省为% g)。
OFS 输出字段分隔符。
ORS 输出记录分隔符。
NF 当前记录中的字段数。
如果你只处理一个文件,则NR 和FNR 的值是一样的。但如果是多个文件, NR是对所有的文件来说的,而FNR 则只是针对当前文件而言。
例子:统计空行的脚本:
[root@master bkeep]# cat for.sh
#!/bin/bash
for i in $@;
do
if [ -f $i ]; then
# echo $i
awk 'BEGIN { printf "%s\t",FILENAME;} //输出文件名
/^$/ { x+=1;} //统计空行
END{avg=100*(x/NR); printf "%s\t% 3.1f\n",x,avg;}' $i // 最后算百分比及打印结果
else
echo "ERROR:$i not a file." >&2
fi
done
[root@master bkeep]# ./for.sh aa bb //显示出来的结果很漂亮。可惜了,文件名没出来!
7 87.5 //怀疑FILENAME内置变量被修改了???
4 57.1
经过前面的学习,我们知道shell变量的引用由$xxx 实现,而awk中的变量直接使用xxx来引用。所以我们就要思考如何让awk使用shell变量
语法:
awk ‘script’ awkvar1=value awkvar2=value ...... files
说明: awk: 命令; ‘script’:awk将要执行的脚本; awkvarN:awk的变量;value:shell中的变量值; files:将要处理的文件名
【重点实验—记忆】
例子:根据用户提供的水果数量,将低于这个值的水果打印出来!
[root@master bkeep]# cat awk_var.sh
#!/bin/bash
NUM_FRUIT="$1" //将用户输入传递给shell变量NUM_FRUIT
if [ -z "$NUM_FRUIT" ]; then NUM_FRUIT=75;fi //如果用户没有输入,则赋默认值75
awk '
$3 <= num_fruit {print;} //最终将用户输入传递给awk变量num_fruit
' num_fruit="$NUM_FRUIT" fruit_prices.txt //通过此方法将$NUM_FRUIT
//赋值给num_fruit;即把shell变量赋给awk
执行结果:
[root@master bkeep]# ./awk_var.sh 40 //将数量低于40的水果打印出来
kiwi $1.50 22
pineapple $1.29 35
[root@master bkeep]# ./awk_var.sh 30 //将数量低于30的水果打印出来
kiwi $1.50 22
if语句
while语句
for语句
if (expression1) {action1}
else if (expression2) {action2}
else { action3}
执行过程:
1)计算expression1的值
2)若expression1为真,执行action1并推出if语句
3)若expression1为假,计算expression2
4)若expression2为真,执行action2并退出if语句
5)若expression2为假,执行action3并退出if
举例:若水果价格高于1美元,那么给它加星号;若水果数量少于50,则给它加REORDER
[root@master bkeep]# cat reorder.sh
#!/bin/bash
awk '{
printf "%s\t",$0;
if ( $2 ~ /\$[1-9][0-9]*\.[0-9][0-9]/ ) {
printf " * ";
if ($3 <= 75) {
printf "REORDER\n";
} else {
printf "\n";
}
}else {
if ($3<=75) {
printf "REORDER\n";
}else {
printf "\n";
}
}
}' fruit_prices.txt
执行结果:
[root@master bkeep]# ./reorder.sh
fruit price/lbs quantity
banana $0.89 100
peach $0.79 65 REORDER
kiwi $1.50 22 * REORDER
pineapple $1.29 35 * REORDER
apple $0.99 78
while (expression) { actions }
若expression为真,则执行actions
[root@master bkeep]# awk 'BEGIN { x=0;while(x<3) {x+=1;print x;}}' //先给x+1再输出
1
2
3
[root@master bkeep]# awk 'BEGIN { x=0;while(x<3) {print x;x++}}' //先输出再给x+1
0
1
2
Tips:do语句是while语句衍生出来的;区别在于do至少执行一次,while有可能不执行
for (initialize_counter; test_counter;increment_counter) {
action
}
解释一下,呵呵
for (初始化计数器变量;测试计数器是否到顶了;增加计数器的值) {
要执行的动作!
}
【常用】例子:循环迭代处理记录中的域并输出他们
[root@master bkeep]# cat for2.sh
#!/bin/bash
awk '{
for (x=1;x<=NF;x+=1) {
printf "%s ",$x "zbb";
}
printf "\n";
}' fruit_prices.txt
[root@master bkeep]# ./for2.sh
fruitzbb price/lbszbb quantityzbb
bananazbb $0.89zbb 100zbb
peachzbb $0.79zbb 65zbb
根据输出我们重点理解下awk中for循环的执行过程
1)读取一行数据
2)执行for循环,处理每一个字段,将其输出,退出for循环
3)执行第二个action即输出一个换行符
4)重复1~3,直到处理完所有行!
例子:倒序显示记录中的每个行
方法一:用for实现
#!/bin/bash
awk '
{a[NR]=$0}; //第一个action:把记录存储在一个数组里面
END{for (x=NR;x>0;x-=1) { //第二个action:for循环,x=NR总行数
printf "%s\n",a[x]; //如果x>0,则x=x-1,倒序输出a[x]数组中的内容!
}
}' fruit_prices.txt //要处理的文件名!
方法2:用while实现
#!/bin/bash
awk '
{line[NR] = $0 } //把所有记录都存放到数组里面
END {var=NR //把总行数赋给变量var
while (var > 0){ //当var大于0时,执行后面的action
print line[var]
var-- //var=var-1
}}
' fruit_prices.txt //文件名
执行结果:
[root@master bkeep]# ./re.sh
apple $0.99 78
pineapple $1.29 35
kiwi $1.50 22
peach $0.79 65
banana $0.89 100
fruit price/lbs quantity
# sh /usr/local/bkeep/shell.sh 001 002
I'm $0 is:/usr/local/bkeep/shell.sh //正在被执行脚本的名字;`basename $0`
I'm $1 is:001 //$1接收到的参数
I'm $2 is:002 //$2接收到的参数
I'm $# is:2 //总共接收到的参数个数
I'm $* is:001 002 //把接收到的参数全部打印出来
I'm $@ is:001 002 //同上
I'm $? is:0 //上一个脚本的退出状态“ 0”代表正常;“ 1”非正常退出
I'm $$ is:24137 //当前执行脚本的进程ID
I'm $! is: //前一个后台进程的id
例子:再进一步解释下$*和$@吧;仔细看下面的例子,我就不废话了!
[root@master bkeep]# cat shell.sh
#!/bin/bash
cc=$*
echo "I'm \$1 is:$1"
echo "I'm \$2 is:$2"
echo "I'm \$* is:$*"
echo "I'm \$4 is:$4"
[root@master bkeep]# ./shell.sh 11 22 33 "44" 55
I'm $1 is:11
I'm $2 is:22
I'm $* is:11 22 33 44 55
I'm $4 is:44
应用例子:统计文件中的空行数
[root@master bkeep]# cat for.sh
#!/bin/bash
for i in $@; //这里我们不知道有多少个文件需要统计,所以用$@来接收参数!
do
if [ -f $i ]; then
echo $i
awk ' /^$/ { x=x+1;} END{print x;}' $i
else
echo "ERROR:$i not a file." >&2
fi
done
[root@master bkeep]# ./for.sh aa bb
aa //文件名为aa
7 //空行数7
bb //文件名为bb
4 //空行数为4