Chinaunix首页 | 论坛 | 博客
  • 博客访问: 372570
  • 博文数量: 80
  • 博客积分: 6032
  • 博客等级: 准将
  • 技术积分: 730
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-05 20:07
文章分类

全部博文(80)

文章存档

2007年(1)

2006年(22)

2005年(57)

我的朋友

分类:

2005-07-05 21:14:49

字符串屏蔽序列

1. 字符转换

2. 格式化输出

3.向一行a w k命令传值

4. awk脚本文件

5. 在awk中使用FS变量

6. 向awk脚本传值

=================================

字符串屏蔽序列

使用字符串或正则表达式时,有时需要在输出中加入一新行或查询一元字符。
打印一新行时,(新行为字符 n),给出其屏蔽序列,以不失其特殊含义,用法为在字符串前加入反斜线。例如使用 n强迫打印一新行。
如果使用正则表达式,查询花括号( { }),在字符前加反斜线,如/ { /,将在a w k中失掉其特殊含义。

代码:
awk中使用的屏蔽序列
b 退格键
t t a b键
f 走纸换页
d d d 八进制值
n 新行
c 任意其他特殊字符,例如 为反斜线符号
r 回车键


使用上述符号,打印May Day,中间夹t a b键,后跟两个新行,再打印May Day,但这次使用八进制数1 0 4、1 4 1、1 7 1、分别代表D、a、y。
代码:
[root@chenwy sam]# awk 'BEGIN {print" May Day May 104141171"}'

May     Day

May     Day

注意, 1 0 4为D的八进制A S C I I码, 1 4 1为a的八进制A S C I I码,等等。

awk输出函数printf
目前为止,所有例子的输出都是直接到屏幕,除了t a b键以外没有任何格式。a w k提供函数p r i n t f,拥有几种不同的格式化输出功能。例如按列输出、左对齐或右对齐方式。
每一种p r i n t f函数(格式控制字符)都以一个%符号开始,以一个决定转换的字符结束.转换包含三种修饰符。
p r i n t f函数基本语法是p r i n t f([格式控制符],参数),格式控制字符通常在引号里。

printf修饰符
代码:
- 左对齐
Wi d t h 域的步长,用0表示0步长
. p r e c 最大字符串长度,或小数点右边的位数
表9-7 awk printf格式
% c A S C I I字符
% d 整数
% e 浮点数,科学记数法
% f 浮点数,例如(1 2 3 . 4 4)
% g a w k决定使用哪种浮点数转换e或者f
% o 八进制数
% s 字符串
% x 十六进制数

1. 字符转换
观察A S C I I码中6 5的等价值。管道输出6 5到a w k。p r i n t f进行A S C I I码字符转换。这里也加入换行,因为缺省情况下p r i n t f不做换行动作。

代码:
A[sam@chenwy sam]$ echo "65" | awk '{printf "%c ",$0}'
A


按同样方式使用a w k得到同样结果。
代码:
[sam@chenwy sam]$ awk 'BEGIN{printf "%c ",65}'
A


所有的字符转换都是一样的,下面的例子表示进行浮点数转换后‘ 9 9 9’的输出结果。整数传入后被加了六个小数点。
代码:
[sam@chenwy sam]$ awk 'BEGIN{printf "%f ",999}'
999.000000


2. 格式化输出
打印所有的学生名字和序列号,要求名字左对齐, 1 5个字符长度,后跟序列号。注意 n换行符放在最后一个指示符后面。输出将自动分成两列。
代码:
[root@chenwy sam]# awk '{printf "%-15s %s ",$1,$3}' grade.txt
M.Tans          48311
J.Lulu          48317
P.Bunny         48
J.Troll         4842
L.Tansl         4712


加入一些文本注释帮助理解报文含义。可在正文前嵌入头信息。注意这里使用p r i n t加入头信息。如果愿意,也可使用p r i n t f。
代码:
[root@chenwy sam]# awk 'BEGIN{print "Name S.Number"}{printf "%-15s %s ",$1,$3}' grade.txt
Name            S.Number
M.Tans          48311
J.Lulu          48317
P.Bunny         48
J.Troll         4842
L.Tansl         4712


3.向一行a w k命令传值
在查看a w k脚本前,先来查看怎样在a w k命令行中传递变量。
在a w k执行前将值传入a w k变量,需要将变量放在命令行中,格式如下:
代码:
awk 命令变量=输入文件值

(后面会讲到怎样传递变量到a w k脚本中)。
下面的例子在命令行中设置变量A G E等于1 0,然后传入a w k中,查询年龄在1 0岁以下的所有学生。
代码:
[root@chenwy sam]# awk '{if ($5M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26


要快速查看文件系统空间容量,观察其是否达到一定水平,可使用下面a w k一行脚本。因为要监视的已使用空间容量不断在变化,可以在命令行指定一个触发值。首先用管道命令将df -k 传入a w k,然后抽出第4列,即剩余可利用空间容量。使用$ 4 ~ / ^ [ 0 - 9 ] /取得容量数值(1 0 2 4块)而不是d f的文件头,然后对命令行与‘ i f ( $ 4 < T R I G G E R )’上变量T R I G G E R中指定
的值进行查询测试。
代码:
[root@chenwy sam]# df -k|awk '{if($4/boot   458589
/dev/shm        99352


代码:
[root@chenwy sam]# df -k|awk '($4~/^[0-9]/) {if($4/       2610716
/boot   458589
/dev/shm        99352


($4~/^[0-9]/)好像没什么用

在系统中使用df -k命令,产生下列信息:
代码:
[root@chenwy sam]# df -k
文件系统               1K-块        已用     可用 已用% 挂载点
/dev/sda2              5162828   2289804   2610764  47% /
/dev/sda1               497829     13538    458589   3% /boot
none                     99352         0     99352   0% /dev/shm


如果系统中d f输出格式不同,必须相应改变列号以适应工作系统。
当然可以使用管道将值传入a w k。本例使用w h o命令, w h o命令第一列包含注册用户名,这里打印注册用户,并加入一定信息。
代码:
[sam@chenwy sam]$ who |awk '{print $1" is logged on"}'
root is logged on
root is logged on
[sam@chenwy sam]$ who
root     :0           Nov 23 20:17
root     pts/0        Nov 23 20:25 (:0.0)


a w k也允许传入环境变量。下面的例子使用环境变量HOME支持当前用户目录。可从pwd命令管道输出到a w k中获得相应信息。
代码:
[sam@chenwy sam]$ pwd | awk '{if ($1==derr) print $1}' derr=$HOME
/usr/sam



4. awk脚本文件
可以将a w k脚本写入一个文件再执行它。命令不必很长(尽管这是写入一个脚本文件的主要原因),甚至可以接受一行命令。这样可以保存a w k命令,以使不必每次使用时都需要重新输入。使用文件的另一个好处是可以增加注释,以便于理解脚本的真正用途和功能。
使用前面的几个例子,将之转换成a w k可执行文件。像原来做的一样,将学生目前级别分相加awk ‘(t o t + = $ 6) END{print "club student total points:" t o t }’ g r a d e . t x t。
创建新文件s t u d e n t _ t o t . a w k,给所有a w k程序加入a w k扩展名是一种好习惯,这样通过查看文件名就知道这是一个a w k程序。文本如下:

代码:
[sam@chenwy sam]$ cat student_tot.awk
#!/bin/awk -f
#all commnet lines must start with a hash '#'
#name:students_tots.awk
#to call:student_tot.awk grade.txt
#prints total and average of club student points

#print a header first
BEGIN{
print "Student  Date  Member  No.  Grade Age  Points Max"
print "Name     Joined                        Gained  Point Available"
print "=============================================================="
}
#let's add the scores of points gained
(tot+=$6)

#finished proessing now let's print the total and average point
END{
print "Club student total points :" tot
print "Average Club Student Points:" tot/NR}


通过将命令分开,脚本可读性提高,还可以在命令之间加入注释。这里加入头
信息和结尾的平均值。基本上这是一个一行脚本文件。
执行时,在脚本文件后键入输入文件名,但是首先要对脚本文件加入可执行权限。
代码:
[sam@chenwy sam]$ chmod u+x student_tot.awk
[sam@chenwy sam]$./student_tot.awk grade.txt
Student  Date  Member  No.  Grade Age  Points Max
Name     Joined                        Gained  Point Available
==============================================================
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
Club student total points :155
Average Club Student Points:31


过滤相同行:
如有一个文件strip中有多条重复错误提法:
代码:
[sam@Linux_chenwy sam]$ cat strip
etreiytrpytyu
ERROR*
ERROR*
ERROR*
ERROR*
IUEWROPYJRTMELUYK
ERROR*
ERROR*
ERROR*
ERROR*
ERROR*
ERROR*
EWUTIRWJYHT
ERROR*
ERROR*
JGIOERYO56ERU
ERROR*
ERROR*
ERROR*
JGEORYKP65EKU;YK,


现在用a w k脚本过滤出错误行的出现频率,使得每一个失败记录只对应一个错误行。awk脚本如下:
代码:
[sam@Linux_chenwy sam]$ cat error_strip.awk
#!/bin/awk -f
#error_strip.awk
#to call:error_strip.awk
#strips out the ERROR* lines if there are more than one
#ERROR* lines after each failed record.

BEGIN {error_line=""}
#tell awk the whole is "ERROR*"
{if ($0=="ERROR*" && error_line=="ERROR*")

#go to next line
next;
error_line=$0;print}


执行结果如下:
代码:

[sam@Linux_chenwy sam]$ ./error_strip.awk strip
etreiytrpytyu
ERROR*
IUEWROPYJRTMELUYK
ERROR*
EWUTIRWJYHT
ERROR*
JGIOERYO56ERU
ERROR*
JGEORYKP65EKU;YK,

5. 在a w k中使用F S变量
如果使用非空格符做域分隔符( F S)浏览文件,例如# 或:,编写这样的一行命令很容易,因为使用F S选项可以在命令行中指定域分隔符。

代码:
$awk -F: '{print $0}' inputfile


使用a w k脚本时,记住设置F S变量是在B E G I N部分。如果不这样做, a w k将会发生混淆,不知道域分隔符是什么。
下述脚本指定F S变量。脚本从/ e t c / p a s s w d文件中抽取第1和第5域,通过分号“;”分隔p a s s w d文件域。第1域是帐号名,第5域是帐号所有者。
我举的例子是第七个域:

代码:
[sam@Linux_chenwy sam]$ awk -F: '{print $1," ",$7}' passwd
root     /bin/bash
bin      /sbin/nologin
daemon   /sbin/nologin
adm      /sbin/nologin
lp       /sbin/nologin
sync     /bin/sync
.................................

这是不用脚本的,后面的结果省略

现使用脚本如下:
代码:
[sam@Linux_chenwy sam]$ cat passwd.awk
#!/bin/awk -f
#to call:passwd.awk /etc/passwd
#print out the first and seventh fields
BEGIN{
FS=":"}
{print $1," ",$7}


结果如下:
代码:
[sam@Linux_chenwy sam]$ chmod u+x passwd.awk
[sam@Linux_chenwy sam]$ ./passwd.awk passwd
root     /bin/bash
bin      /sbin/nologin
daemon   /sbin/nologin
adm      /sbin/nologin
lp       /sbin/nologin
sync     /bin/sync
.......................................



6. 向a w k脚本传值
向a w k脚本传值与向a w k一行命令传值方式大体相同,格式为:
代码:
awk script_file var=value input_file


下述脚本对比检查文件中域号和指定数字。这里使用了N F变量M A X,表示指定检查的域号,使用双引号将域分隔符括起来,即使它是一个空格。
脚本如下:
代码:
[sam@Linux_chenwy sam]$ cat fieldcheck.awk
#!/bin/awk -f
#check on how many fields in a file
#name:fieldcheck.awk
#to call:fieldcheck MAX=n FS= filename
#
NF!=MAX{
print("line" NR " does not have " MAX "fields")}

如果NF中的值不等于最大MAX值,则打印出"哪一行的域总数不是max"

如果以/ e t c / p a s s w d作输入文件(p a s s w d文件有7个域),运行上述脚本。参数格式如下:
代码:
[sam@Linux_chenwy sam]$ chmod u+x fieldcheck.awk
[sam@Linux_chenwy sam]$ ./fieldcheck.awk MAX=7 FS=":" passwd

正好7个域,如果改成6,就会显示不同结果,试试看?

使用前面一行脚本的例子,将之转换成a w k脚本如下:
代码:
[sam@Linux_chenwy sam]$ cat name.awk
#!/bin/awk -f
#name:age.awk
#to call:age.awk AGE=n grade.txt
#print ages that are lower than the age supplied on the comand line
{if ($5print $0}


文本包括了比实际命令更多的信息,没关系,仔细研读文本后,就可以精确知道其功能及如何调用它。
不要忘了增加脚本的可执行权限,然后将变量和赋值放在命令行脚本名字后、输入文件前执行。
代码:
[sam@Linux_chenwy sam]$ chmod u+x name.awk
[sam@Linux_chenwy sam]$ ./name.awk AGE=10 grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26


同样可以使用前面提到的管道命令传值,下述a w k脚本从d u命令获得输入,并输出块和字节数。
代码:
[root@Linux_chenwy sam]# cat duawk.awk
#!/bin/awk -f
#to call:du|duawk.awk
#prints file/direc's in bytes and blocks
BEGIN{
OFS=" ";
print "name" " ","bytes","blocks "
print "==============================="}
{print $2," ",$1*512,$1}


使用du的结果如下
代码:
[root@Linux_chenwy sam]# du
12      ./.kde/Autostart
16      ./.kde
8       ./.xemacs
4       ./sam
4       ./dir1
4       ./file6
184     .


执行:
代码:
[root@Linux_chenwy sam]# du | ./duawk.awk
name                    bytes   blocks

===============================
./.kde/Autostart                                6144    12
./.kde                          8192    16
./.xemacs                               4096    8
./sam                           2048    4
./dir1                          2048    4
./file6                         2048    4
.                               94208   184


代码:

OFS=" ";

数组
前面讲述s p l i t函数时,提到怎样使用它将元素划分进一个数组。这里还有一个例子:

代码:
[sam@Linux_chenwy sam]$ awk 'BEGIN {print split("123#456#789",myarray,"#")}'
3

实际上m y a r r a y数组为
代码:
Myarray[1]="123"
Myarray[2]="456"
Myarray[3]="789"


数组使用前,不必定义,也不必指定数组元素个数。经常使用循环来访问数组。下面是一种循环类型的基本结构:
代码:
For (element in array ) print array[element]


对于记录“ 1 2 3 # 4 5 6 # 6 7 8”,先使用s p l i t函数划分它,再使用循环打印各数组元素。操作脚本如下:
代码:
[sam@Linux_chenwy sam]$ cat arraytest.awk
#!/bin/awk -f
#name:arraytest.awk
#prints out an array
BEGIN{
record="123#456#789";
split(record,myarray,"#")}
END{for (i in myarray) {print myarray[i]}}


要运行脚本,使用/ d e v / n u l l作为输入文件。
代码:
sam@Linux_chenwy sam]$chmod u+x arraytest.awk
[sam@Linux_chenwy sam]$ ./arraytest.awk /dev/null
123
456
789
[sam@Linux_chenwy sam]$



数组和记录
上面的例子讲述怎样通过s p l i t函数使用数组。也可以预先定义数组,并使用它与域进行比较测试,下面的例子中将使用更多的数组。
下面是从空手道数据库卸载的一部分数据,包含了学生级别及是否是成人或未成年人的信息,有两个域,分隔符为( #),文件如下:
代码:
[sam@Linux_chenwy sam]$ cat grade_student.txt
Yellow#Junior
Orange#Senior
Yellor#Junior
Purple#Junior
Brown-2#Junior
White#Senior
Orange#Senior
Red#Junior
Red#Junior
Brown-2#Senior
Yellow#Senior
Red#Junior
Blue#Senior
Green#Senior
Purple#Junior
White#Junior


脚本功能是读文件并输出下列信息。
1) 俱乐部中Ye l l o w、O r a n g e和R e d级别的人各是多少。
2 ) 俱乐部中有多少成年人和未成年人。
查看文件,也许2 0秒内就会猜出答案,但是如果记录超过6 0个又怎么办呢?这不会很容易就看出来,必须使用a w k脚本。
首先看看a w k脚本,然后做进一步讲解。

代码:
[sam@Linux_chenwy sam]$ cat belts.awk
#!/bin/awk -f
#name:belts.awk
#to call:belts.awk grade2.txt
#loops through the grade2.txt file and counts how many
#belts we have in (yellow,orange,red)
#also count how many adults and juniors we have
#
#start of BEGIN
#set FS and load the arrays with our values

#B E G I N部分设置F S为符号#,即域分隔符

BEGIN{FS="#"

#Load the belt colours we are interested in only
#因为要查找Ye l l o w、O r a n g e和R e d三个级别。
#然后在脚本中手工建立数组下标对学生做同样的操作。
#注意,脚本到此只有下标或元素,并没有给数组名本身加任何注释。

belt["Yellow"]
belt["Orange"]
belt["Red"]
#end of BEGIN
#load the student type
student["Junior"]
student["Senior"]
}

##初始化完成后, B E G I N部分结束。记住B E G I N部分并没有文件处理操作。

#loop thru array that holds the belt colours against field-1
#if we have a match,keep a running total

#现在可以处理文件了。
#首先给数组命名为c o l o r,使用循环语句测试域1级别列是否
#等于数组元素之一(Ye l l o w、O r a n g e或R e d),
#如果匹配,依照匹配元素将运行总数保存进数组。

{for (colour in belt)
{if($1==colour)
belt[colour]++}}

#loop thru array that holds the student type against
#field-2 if we have a match,keep a runing total

#同样处理数组‘ S e n i o r _ o r _ j u n i o r’,
#浏览域2时匹配操作满足,运行总数存入j u n i o r或s e n i o r的匹配数组元素。

{for (senior_or_junior in student)
{if ($2==senior_or_junior)
student[senior_or_junior]++}}

#finished processing so print out the matches..for each array

#E N D部分打印浏览结果,对每一个数组使用循环语句并打印它。

END{for (colour in belt )print "The club has ",belt[colour],colour,"Belts"

#注意在打印语句末尾有一个符号,用来通知a w k(或相关脚本)命令持续到下一行,
#当输入一个很长的命令,并且想分行输入时可使用这种方法。

for (senior_or_junior in student) print "The club has ",
student[senior_or_junior],senior_or_junior,"student"}


运行脚本前记住要加入可执行权限

代码:
[sam@Linux_chenwy sam]$ chmod u+x belts.awk
[sam@Linux_chenwy sam]$ ./belts.awk grade_student.txt
The club has  3 Red Belts
The club has  2 Orange Belts
The club has  2 Yellow Belts
The club has  7 Senior student
The club has  9 Junior student

阅读(789) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~