写了很长时间脚本,awk这块遇到过很多问题,下面这三个问题解决后
再也没有遇到过什么大的困难(至少目前),后来很多次割接,数据割接部分我都是用awk来做,比c,c++方便100倍
一、awk 与 shell 交互方式
在平时写脚本的过程中, awk 与 shell的交互不可避免的, shell比较强大, 但在处理一些事情时使用awk比较方便,
例如:
1、shell不支持小数点运算, awk支持
2、awk以字段的方式处理数据非常强大
3、awk处理文件关联比较方便
等等
下面列出几种常用的关联方式:
a、管道交互
这种方式最常见,举几个例子(个人写脚本喜欢换行,ksh可支持):
a.1:kill进程:
ps -ef | grep MonitorFrame | grep -v grep |
awk '
{
print "kill -9",$2;
}
'|sh
a.2: 清楚共享内存:
ipcs | grep gzngcrm |
awk '
{
if($1=="s"){
print "ipcrm", "-S", $3;
}
if($1=="m"){
print "ipcrm", "-M", $3;
}
}' | sh
a.3: 删除一个目录下的大量文件
当一个目录下的文件数过多时, rm * 会报入参超过数量
一般可以这样处理 ls | xargs rm
但如果文件名中含有空格, 比如我们的dump文件: 如MVPMNACCEPT_IVPMNSO_BAVDFQ06 - OUT - 2010-09-26 17-30-59.15-4492.dump
这时就需要awk了
ls | awk '{ print "rm", "\""$0"\"";}'|sh
b、变量交互
举例:
shell脚本在运行的过程中,得到了变量$m, $n的值, 需要求$m/$n , 但shell并不支持小数运算, 传入给awk计算
有两种方式:
b.1: -v 方式
awk -v mm="$m" -v nn="$n" '
BEGIN{
print mm/nn;
}
' | read c
echo $c;
b.2:"'$变量'" 方式
awk '
BEGIN{
print "'$m'"/"'$n'";
}
'| read c
echo $c
c、awk内部的交互
awk '
BEGIN{
while("ls|sort"|getline name){
print "hello"name|"sort"
}
}
'
二、delete 回收内存
08年的时候, 局方提出了个很变态的提数需求, 记得是提07年12月到08年11月之间的广州全球通用户欠费情况
还要求附上相关的其他不着边际的信息, 总之要关联很多张表, 又不让直接在数据库中操作,怕影响数据库,
当时遇到了一个需要及时回收内存的操作, 大概如下:
输入:
subsid|validbillcyc|amt|
111|200712|50
111|200802|30
222|200801|20
111|200801|100
111|200802|50
222|200711|20
111|200712|20
111|200711|20
这里只列举其中两个用户,实际文件相当庞大。
要求输出:
subsid|200712|200801|200802|
111|90|100|80|
222|20|20|0|
计算出每个用户200712月以前的欠费(含12月), 200801月欠费, 200802月欠费
按照常规按subsid&validbillcyc 建hash进行累加, 这样速度会慢的不得了
当时有个现成的归并排序工具, 可按其中一列进行排序, 所以进行排序(如下)
因此可以排队累加(按 subsid + validbillcyc)
111|200712|50
111|200802|30
111|200801|100
111|200802|50
111|200712|20
111|200711|20 --对111用户来说, 每次累加只需要累加到这里
222|200801|20
222|200711|20 --同样
111用户的累加结果为:
money[111|200712] = 90;
money[111|200802] = 80;
money[111|200801] = 100;
输出:
111|90|100|80|
按道理这样做就没有问题了, 但做的过程中,报内存不够
数据量非常大, 脚本在往下处理的过程中, 并没有及时释放之前的 money[xxx|YYYYMM] 的内存
因此需要及时释放内存:
for(i in money){
delete money ;
}
---脚本:
awk -F\| '
BEGIN{
print "subsid|200712|200801|200802|";
}
{
if(NR==1){
onesubsid = $1;
if($2 <= "200712"){
money[onesubsid"|200712"] += $3;
}
else{
money[onesubsid"|"$2] += $3;
}
}
else{
if(onesubsid == $1){
if($2 <= "200712"){
money[onesubsid"|200712"] += $3;
}
else{
money[onesubsid"|"$2] += $3;
}
}
else{
money200712 = money[onesubsid"|200712"];
money200801 = money[onesubsid"|200801"];
money200802 = money[onesubsid"|200802"];
printf("%s|%.3f|%.3f|%.3f|\n",onesubsid,money200712,money200801,money200802);
for(i in money){
delete money;
}
onesubsid = $1;
if($2 <= "200712"){
money[onesubsid"|200712"] += $3;
}
else{
money[onesubsid"|"$2] += $3;
}
}
}
}
END{
money200712 = money[onesubsid"|200712"];
money200801 = money[onesubsid"|200801"];
money200802 = money[onesubsid"|200802"];
printf("%s|%.3f|%.3f|%.3f|\n",onesubsid,money200712,money200801,money200802);
for(i in money){
delete money;
}
}
' unwoffcust
三、大文件关联
文件1格式: subsid|recamt|
文件2格式: subsid|servnumber|
输出: subsid|recamt|servnumber|
把文件1读入内存,建立hash money[subsid]=recamt
然后逐行读文件2, 根据subsid去money中找就可以了
问题在于文件1和文件2都非常庞大, awk所支持的hash不能支持 --这里就体现了perl的优势,但perl用的人太少
解决方法:
将文件1,文件2 都按subsid排序
从文件1中读入1行, 然后去文件2中找,如果小(继续往下找), 如果相等(找到,下次从文件2的这里的下一行开始找),
如果大(没找到,下次从文件2这里开始找)
文件1 文件2
100|10 98|13611111111 --100从这里开始
102|20 99|13611111111
105|30 100|13611111111 --找到
101|13611111111 --102从这里开始
103|13611111111 --没找到, 105从这里开始
104|13611111111
105|13611111111 --找到
awk -F\| -v subsfile='cm_subs_subscriber' '
BEGIN{
getline < subsfile; #: $0, $1, $2
subsid = $1;
senumr = $2;
}
{
this_subsid = $1;
this_amt = $2;
while(this_subsid > subsid){ #$1,$2 取最新的一次读入的值
getline < subsfile;
subsid = $1;
senumr = $2;
}
if(this_subsid == subsid){
printf("%s|%s|%s|\n",this_subsid,this_amt,senumr);
}
else{
printf("%s|%s|%s|\n",this_subsid,this_amt,"-1");
}
}
' ib_cb_unwoffcust > ib_cb_unwoffcust_servnumber
再也没有遇到过什么大的困难(至少目前),后来很多次割接,数据割接部分我都是用awk来做,比c,c++方便100倍
一、awk 与 shell 交互方式
在平时写脚本的过程中, awk 与 shell的交互不可避免的, shell比较强大, 但在处理一些事情时使用awk比较方便,
例如:
1、shell不支持小数点运算, awk支持
2、awk以字段的方式处理数据非常强大
3、awk处理文件关联比较方便
等等
下面列出几种常用的关联方式:
a、管道交互
这种方式最常见,举几个例子(个人写脚本喜欢换行,ksh可支持):
a.1:kill进程:
ps -ef | grep MonitorFrame | grep -v grep |
awk '
{
print "kill -9",$2;
}
'|sh
a.2: 清楚共享内存:
ipcs | grep gzngcrm |
awk '
{
if($1=="s"){
print "ipcrm", "-S", $3;
}
if($1=="m"){
print "ipcrm", "-M", $3;
}
}' | sh
a.3: 删除一个目录下的大量文件
当一个目录下的文件数过多时, rm * 会报入参超过数量
一般可以这样处理 ls | xargs rm
但如果文件名中含有空格, 比如我们的dump文件: 如MVPMNACCEPT_IVPMNSO_BAVDFQ06 - OUT - 2010-09-26 17-30-59.15-4492.dump
这时就需要awk了
ls | awk '{ print "rm", "\""$0"\"";}'|sh
b、变量交互
举例:
shell脚本在运行的过程中,得到了变量$m, $n的值, 需要求$m/$n , 但shell并不支持小数运算, 传入给awk计算
有两种方式:
b.1: -v 方式
awk -v mm="$m" -v nn="$n" '
BEGIN{
print mm/nn;
}
' | read c
echo $c;
b.2:"'$变量'" 方式
awk '
BEGIN{
print "'$m'"/"'$n'";
}
'| read c
echo $c
c、awk内部的交互
awk '
BEGIN{
while("ls|sort"|getline name){
print "hello"name|"sort"
}
}
'
二、delete 回收内存
08年的时候, 局方提出了个很变态的提数需求, 记得是提07年12月到08年11月之间的广州全球通用户欠费情况
还要求附上相关的其他不着边际的信息, 总之要关联很多张表, 又不让直接在数据库中操作,怕影响数据库,
当时遇到了一个需要及时回收内存的操作, 大概如下:
输入:
subsid|validbillcyc|amt|
111|200712|50
111|200802|30
222|200801|20
111|200801|100
111|200802|50
222|200711|20
111|200712|20
111|200711|20
这里只列举其中两个用户,实际文件相当庞大。
要求输出:
subsid|200712|200801|200802|
111|90|100|80|
222|20|20|0|
计算出每个用户200712月以前的欠费(含12月), 200801月欠费, 200802月欠费
按照常规按subsid&validbillcyc 建hash进行累加, 这样速度会慢的不得了
当时有个现成的归并排序工具, 可按其中一列进行排序, 所以进行排序(如下)
因此可以排队累加(按 subsid + validbillcyc)
111|200712|50
111|200802|30
111|200801|100
111|200802|50
111|200712|20
111|200711|20 --对111用户来说, 每次累加只需要累加到这里
222|200801|20
222|200711|20 --同样
111用户的累加结果为:
money[111|200712] = 90;
money[111|200802] = 80;
money[111|200801] = 100;
输出:
111|90|100|80|
按道理这样做就没有问题了, 但做的过程中,报内存不够
数据量非常大, 脚本在往下处理的过程中, 并没有及时释放之前的 money[xxx|YYYYMM] 的内存
因此需要及时释放内存:
for(i in money){
delete money ;
}
---脚本:
awk -F\| '
BEGIN{
print "subsid|200712|200801|200802|";
}
{
if(NR==1){
onesubsid = $1;
if($2 <= "200712"){
money[onesubsid"|200712"] += $3;
}
else{
money[onesubsid"|"$2] += $3;
}
}
else{
if(onesubsid == $1){
if($2 <= "200712"){
money[onesubsid"|200712"] += $3;
}
else{
money[onesubsid"|"$2] += $3;
}
}
else{
money200712 = money[onesubsid"|200712"];
money200801 = money[onesubsid"|200801"];
money200802 = money[onesubsid"|200802"];
printf("%s|%.3f|%.3f|%.3f|\n",onesubsid,money200712,money200801,money200802);
for(i in money){
delete money;
}
onesubsid = $1;
if($2 <= "200712"){
money[onesubsid"|200712"] += $3;
}
else{
money[onesubsid"|"$2] += $3;
}
}
}
}
END{
money200712 = money[onesubsid"|200712"];
money200801 = money[onesubsid"|200801"];
money200802 = money[onesubsid"|200802"];
printf("%s|%.3f|%.3f|%.3f|\n",onesubsid,money200712,money200801,money200802);
for(i in money){
delete money;
}
}
' unwoffcust
三、大文件关联
文件1格式: subsid|recamt|
文件2格式: subsid|servnumber|
输出: subsid|recamt|servnumber|
把文件1读入内存,建立hash money[subsid]=recamt
然后逐行读文件2, 根据subsid去money中找就可以了
问题在于文件1和文件2都非常庞大, awk所支持的hash不能支持 --这里就体现了perl的优势,但perl用的人太少
解决方法:
将文件1,文件2 都按subsid排序
从文件1中读入1行, 然后去文件2中找,如果小(继续往下找), 如果相等(找到,下次从文件2的这里的下一行开始找),
如果大(没找到,下次从文件2这里开始找)
文件1 文件2
100|10 98|13611111111 --100从这里开始
102|20 99|13611111111
105|30 100|13611111111 --找到
101|13611111111 --102从这里开始
103|13611111111 --没找到, 105从这里开始
104|13611111111
105|13611111111 --找到
awk -F\| -v subsfile='cm_subs_subscriber' '
BEGIN{
getline < subsfile; #: $0, $1, $2
subsid = $1;
senumr = $2;
}
{
this_subsid = $1;
this_amt = $2;
while(this_subsid > subsid){ #$1,$2 取最新的一次读入的值
getline < subsfile;
subsid = $1;
senumr = $2;
}
if(this_subsid == subsid){
printf("%s|%s|%s|\n",this_subsid,this_amt,senumr);
}
else{
printf("%s|%s|%s|\n",this_subsid,this_amt,"-1");
}
}
' ib_cb_unwoffcust > ib_cb_unwoffcust_servnumber