awk & sed 入门

awk:

from:

http://blog.csdn.net/blackbillow/article/details/3847425

作为技术支持工程师,我们最最经常的工作就是要处理文本文件,不管是什么数据库最后都可以导成文本,我们就可以对他进行处理了,这样即使你不是对所有数据库操作都很熟悉,也可以对他的数据进行处理了。

我们必须的工具有两个:一个是shell,一个是awk。awk对于处理文本文件是最最适合的,掌握了awk我们就可以很方便的处理文本文件;再借助一些shell命令,我们可以很方便得到自己想要的结果。现在从简单的例子来总结一下我觉得会经常碰到的问题。


awk入门篇

 awk入门,文本内容example1.txt.
    
user1 password1 username1 unit1 10
user2 password2 username2 unit2 20
user3 password3 username3 unit3 30  

 

在unix环境中我们可以使用一下命令来打印出第一列
    
[root@mail awk]# awk '{print $1}' example1.txt

    得到的结果是如下,解释一下"'{" 单引号大括号包含awk语句是为了和shell 命令区别,$1的意思就是文本文件的第一列,正常的awk命令跟随-F参数指定间隔符号,如果是空格或tab键就可以省略了。    
user1
user2
user3 

 

[root@mail awk]# awk '{if($5>20) {print $1}}' example1.txt

        这行命令和上一行比较增加了“if($5>20)”,得到的结果是

user3

 

这个if语句就没有必要更详细的解释了吧!就是第5列大于20的显示出满足条件的第一列

[root@mail awk]# awk '{if($5>20 || $5==10) {print $1}}' example1.txt

user1
user3

在来一个初级的又增加了一个“if($5>20 || $5==10)”    做逻辑判断逻辑判断的三个“|| && !” 或、与、非三个可以任意加到里面,这个语句的意思是如果第5列大于20或者等于10的都显示处理,在我们的工作中可能有用户会要求找出所有空间大于多少的或者是空间等于多少的账户然后再做批量修改。

 

if是awk循环中的一个还有其他很多,man awk可以看到,
   Control Statements
       The control statements are as follows:

              if (condition) statement [ else statement ]
              while (condition) statement
              do statement while (condition)
              for (expr1; expr2; expr3) statement
              for (var in array) statement
              break
              continue
              delete array[index]
              delete array
              exit [ expression ]
              { statements }


学习awk可以经常使用一下man awk 可以看到所有的函数和使用方法。   

了解每个符号的意义我们才能更好的使用awk,最开始先记住几个命令知道他可实现的结果我们慢慢的再去理解。


awk中级篇

这里顺便介绍一下vi的一个替换命令,现在我们要把example1.txt文本里的空格都替换为“:”冒号这里在vi里使用的命令就是:

%s/ /:/g     

这个命令对于使用vi的人来说是用得最多的。我们现在做了一个新的文件example2.txt。

user1:password1:username1:unit1:10
user2:password2:username2:unit2:20
user3:password3:username3:unit3:30

现在我们来做一个awk脚本,之前都是在命令行操作,实际上所有的操作在命令行上是可以都实现的,已我们最经常使用的批量添加用户来开始!

script1.awk

#!/bin/awk -f   # 当文件有可执行权限的时候你可以直接执行
                # ./script1.awk example2.txt
                # 如果没有以上这行可能会出现错误,或者
                # awk -f script1.awk example2.txt 参数f指脚本文件

BEGIN {         # “BEGIN{”是awk脚本开始的地方
    FS=":";     # FS 是在awk里指分割符的意思
}

{                                # 接下来的“{” 是内容部分
      print "add {";             # 以下都是使用了一个awk函数print 
      print "uid=" $1;
      print "userPassword=" $2;
      print "domain=eyou.com" ;
      print "bookmark=1";
      print "voicemail=1";
      print "securemail=1"
      print "storage=" $5;
      print "}";
      print ".";
}                               # “}”    内容部分结束
END {                           # “END{” 结束部分
    print "exit";
}

执行结果
[root@mail awk]# awk -f script1.awk example2.txt
add {
uid=user1
userPassword=password1
domain=eyou.com
bookmark=1
voicemail=1
securemail=1
storage=10
}
.               
.
.
.
.
.
exit

文本操作就是更方便一些。

下面给两个返回效果一样的例子
[root@mail awk]# awk -F: '{print $1"@"$2}' example2.txt
[root@mail awk]# awk -F: '{printf "%s@%s/n",$1,$2}' example2.txt

user1@password1

 

这里的区别是使用print 和printf的区别,printf格式更自由一些,我们可以更加自由的指定要输出的数据,print会自动在行尾给出空格,而printf是需要给定" /n"的,如果感兴趣你可以把“/n”去掉看一下结果。%s代表字符串%d 代表数字,基本上%s都可以处理了因为在文本里一切都可以看成是字符串,不像C语言等其他语言还要区分数字、字符、字符串等。

 

awk还有一些很好的函数细细研究一下还是很好用的。


这次碰到了一个问题客户有一个用户列表,大概有2w用户,他有一个有趣的工作要做,就是把每个账户目录放到特定的目录下,例如13910011234这个目录要放到139/10/这个目录下,从这里可以看出规律是手机号码的前三位是二级目录名,手机的第3、4为是三级目录名,我们有的就只有一个用户列表,规律找到了我们现在开始想办法处理吧。

example3.txt

13910011234      
15920312343
13922342134
15922334422
......

第一步是要找到一个方法来吧,就是要把每一个手机号分开,最初可能你就会想到这个也没有任何间隔,我们怎么用awk分开他们呢?说实话最初我也考虑了20多分钟,后来想起原来学习python的时候有split函数可以分就想找找awk里是不是有类似的函数,man awk 发现substr 这个函数子串,

[root@mail awk]# awk '{print substr($1,1,3)}'  example3.txt

[root@mail awk]# awk '{printf "%s/%s/n",substr($1,1,3),substr($1,4,2)}'  example3.txt

[root@mail awk]# awk '{printf "mv %s %s/%s/n",$1,substr($1,1,3),substr($1,4,2)}'  example3.txt

以上的两步的返回自己做一下,最后我们就得到了我们想要的结果。

mv 13910011234 139/10
mv 15920312343 159/20
mv 13922342134 139/22
mv 15922334422 159/22

把这部分输出拷贝到一个shell脚本里,在数据当前目录下执行就可以了!

substr(s, i [, n])      Returns  the at most n-character substring of s
                               starting at i.  If n is omitted, the rest of  s
                               is used.
                               
substr函数解释,s代表我们要处理的字符串,i 是我们从这个字符串的第几个位置上开始,n 是我们从开始的位置取多少个字符。多看看man英文也会有所提高的。                              

awk有很多有趣的函数如果感兴趣可以自己去查查看,
man awk
String Functions  字符串函数,举几个觉得常用的函数
    length([s])             Returns  the  length  of  the  string s, or the
                               length of $0 if s is not supplied.
    length 你可以得到字符串的长度,这个是比较常用的一个函数                          
    split(s, a [, r])       Splits the string s into the  array  a  on  the
                               regular expression r, and returns the number of
                               fields.  If r is omitted, FS is  used  instead.
                               The   array  a  is  cleared  first.   Splitting
                               behaves   identically   to   field   splitting,
                               described above.     
                               
        tolower(str)            Returns  a copy of the string str, with all the
                               upper-case  characters  in  str  translated  to
                               their  corresponding  lower-case  counterparts.
                               Non-alphabetic characters are left unchanged.
                               
        toupper(str)            Returns a copy of the string str, with all  the
                               lower-case  characters  in  str  translated  to
                               their  corresponding  upper-case  counterparts.
                               Non-alphabetic characters are left unchanged.
                                                                                    Time Functions  时间函数,我们最最常用到的是时间戳转换函数
                                                                                   
strftime([format [, timestamp]])
                 Formats  timestamp  according to the specification in format.
                 The timestamp should be of the same form as returned by  sys-
                 time().   If timestamp is missing, the current time of day is
                 used.  If format is missing, a default format  equivalent  to
                 the output of date(1) is used.  See the specification for the
                 strftime() function in ANSI C for the format conversions that
                 are  guaranteed  to be available.  A public-domain version of
                 strftime(3) and a man page for it come  with  gawk;  if  that
                 version  was  used to build gawk, then all of the conversions
                 described in that man page are available to gawk.                                                                                   
                                                                                   
这里举例说明时间戳函数是如何使用的

[root@ent root]# date +%s | awk '{print strftime("%F %T",$0)}'
2008-02-19 15:59:19        

我们先使用date命令做一个时间戳,然后再把他转换为时间                                                                                              
还有一些我们现在可能不经常用到的函数,详细内容man awk 自己可以看一下。
 Bit Manipulations Functions   二进制函数
 Internationalization Functions  国际标准化函数
 
 USER-DEFINED FUNCTIONS      用户也可以自己定义自己的函数,感兴趣自己可以再深入研究一下。
 
 For example:

              function  f(p, q,     a, b)   # a and b are local
              {
                   ...
              }

              /abc/     { ... ; f(1, 2) ; ... }
 DYNAMICALLY LOADING NEW FUNCTIONS  动态加载新函数,这个可能就更高级一些了!


 awk高级篇

 
 不管学习任何语言,我们学到的都是工具,工具知道的越多,我们做起工作来就越方便,但是工具在你的手里并不一定能造出好的产品,编辑脚本和编程序也是一样的重要的是算法,别人不知道怎么处理的问题你要知道如何处理。这才能证明你比别人更高,工具只要你慢慢练习都会使用。
 
    下面给大家一个我认为是比较高级的问题了,感兴趣的可以自己再想想更好的解决办法。问题是这样的我们有一个从ldap里导出的文件,它都是一行一个字段来说明的,每个用户的数据是已空行分割的。我们必须把对应的uid 和userPassword找出来而且是对应的。
    
    例子:example4.txt
    
dn: uid=cailiying,domain=ccc.com.cn,o=mail.ccc.com.cn
uid: cailiying
userPassword:: e21kNX0zREl4VEIwODBJdXZkTnU3WFFtS3lRPT0=
letters: 300
quota: 100

dn: uid=caixiaoning,domain=ccc.com.cn,o=mail.ccc.com.cn
userPassword:: e21kNX1kejFXU0doZWprR2RNYnV5ajJJRWl3PT0=
letters: 300
quota: 100
uid: chenzheng
domain: cqc.com.cn

dn: uid=caixiaoning,domain=ccc.com.cn,o=mail.ccc.com.cn
userPassword:: e21kNX1kejFXU0doZWprR2RNYnV5ajJJRWl3PT0=
letters: 300
quota: 100

dn: uid=caixiaoning,domain=ccc.com.cn,o=mail.ccc.com.cn
userPassword:: e21kNX1kejFXU0doZWprR2RNYnV5ajJJRWl3PT0=
letters: 300
quota: 100
uid: chenzheng
domain: cqc.com.cn
                                                                                    处理这个文本我们需要考虑的问题是:
1 uid  和userPassword 并不是每一个段落里都有
2 在每一段里面uid和userPassword 先后顺序是随机的
3 有的段落里可能只有uid 或者只有userPassword

从文本上分析可以看出必须使用的间隔符号,一个是空行,一个是冒号。
冒号我们awk -F:就可以了,不过空行我们不好判断现在想到length()这个函数,在unix里空行最多只有一个/n字符,如果一行字符数小于2我们判断为空行,好现在间隔符号问题解决,空行只能通过循环来实现对空行的判断。                                        

现在碰到的另外一个问题是我们的某个段里的信息是不完全的,我们就要放弃这段这儿如何来做,就是要做两个标记变量u 和 p  再做一个循环如果u  和  p 同事满足我们才输出结果下面的awk脚本就是通过这个思考来解决ldif文本的处理的!

# 此脚本的目的是方便我们以后导ldap的其他邮件的数据,
# 我们之前使用slapdcat -l 导出所有信息,然后我们需要
# 整理出uid  password , 这里的设置都是默认以":" 间隔的
# 例slapcat -l user.ldif  如果想得到一份uid 和userPassword 对应的文件,
# 修改username = "dn"; password = "userpassword"; awk -f ldap2txt.awk user.ldif | grep uid | more  可以查看结果 (有可能是多域的邮件)
# 如果想得到domain 所对应的密码,修改username = "dn"; password = "userpassword";  运行 awk -f ldap2txt.awk user.ldif |grep domain | more


#!/bin/awk -f 
# File name: ldap2txt.awk

BEGIN {
        FS = ":";
        username = "uid";
        password = "userPassword";
}

{

        if(length($0) == 0 ) 
        {
                if (name != "u"  &&  pword != "p")
                {
                        printf ("%s:%s/n", name,pword); 
                        name = "u";
                        pword = "p";
                }
        }

        else  
        {
                if ($1 == username)
                {
                name = "u";
                name = $0; 
                }
                else if($1 == password)
                {
                pword = "p";
                pword = $0;
                }
        }
}
END {

}

实际上对于学习语言首先是熟悉一些常用的函数,然后就是试着去解决别人解决过的问题,然后自己再思考一下是不是有更好,速度更快的解决办法,实际上大部分的程序员都是在重复的使用着别人好的解决办法,把别人的方法转变为自己的方法,就是反复练习解决不同的问题,思考更好的方法!




sed快速入门札记

from:http://www.blogbus.com/public/tb.php/5099097/59552441/556931f1758468e55d75145767c03557


详尽的sed技术可参考O'Reilly出版社的sed & awk一书。

sed摘要

sed和awk起源于单行文本编辑器ed,可以用脚本进行文本批量编辑工作。sed支持正则表达式的基本字符集,但不支持扩展元字符集(extended Metacharacters)。也就是说"?","+"等元字符是不能用的(区别于awk和egrep)。

sed的工作原理:Copy文本到Pattern Space->一行一行的处理->输出。

sed不会对原文本做出改变,默认输出在屏幕上,可用重定向储存在文本中,但是不能直接重定向于原文件。可通过编写shell脚本来直接覆盖源文件(强烈不推荐)。

快速上手

sed命令的基本语法:[address] { [pattern]operation }

[     ]表示可选项。

address是所需处理的文本行的范围,可省略,默认状态下是处理整个文本。例如1代表第一行,$代表最后一行,2,6代表第2行至第6行。

pattern表示需要处理的行的特征,用正则表达式描述。

address和pattern的关系:前者划定大范围,后者精确定位,后者从属于前者。因为两者都是定位的,所以可省略,默认处理每一行。

operation是对文本的进行的操作。常用的有substitution(替换),deletion(删除),print(打印)等。

拿sed & awk中的例子来说,建立一个list文本:

hn Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

如果要将文本中所有的MA替换成Massachusetts,在shell下键入
sed 's/MA/Massachusetts/' list

那么,只要替换从第二行往后的MA呢?
sed '2,${s/MA/Massachusetts/}' list

更苛刻一点,从第二行开始,替换有Eric出现的那一行的MA呢?
sed '2,${/Eric/s/MA/Massachusetts/}' list

反过来,匹配除了有Eric的那一行:
sed '/Eric/!s/MA/Massachusetts/' list

现在,要将一行里所有的","替换为".”,这里要用到全局控制变量g,因为默认状态下只替换 每行匹配中出现的第一个(注意是每行,因为默认情况下,sed会处理所有的行,但只处理每行第一个匹配项;这一点要区别于ed中的g)
sed 's/,/./'g list

了解了替换的用法,删除、打印也水到渠成
sed '1,6{/MA/d}' list #删除1~6行含有MA的行
sed -n '1,6{/CA/p}' list  #打印含CA的行。由于默认条件下sed会输出每一行的内容,故用-n抑制默认输出

重复使用度高的命令,可写成脚本。例如:
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, OKlahoma/
保存在sedscr中,运行时加入-f参数
sed -f sedscr list
即可把所有缩写替换为全称。

高级应用
参考sed & awk一书。

一些小技巧
如果需要编写指令集编写文本,最好先从原文本取一个小的sample,然后依次一条指令一条指令测试。这是sed & awk一书作者的建议,我强烈支持。虽然看起来很麻烦且不必要,但是文本中往往存在很多意想不到的陷阱,而且一旦出错,也得一条一条指令的去掉来debug。

比对两个文件的不同,可以检查脚本做了些什么。
diff file1 file2

总结
语法:[address] { [pattern]operation }
常用命令:s(替换)、d(删除)、p(打印)
常用参数:g(全局替换)、n(抑制默认输出)、f(执行脚本)、!(逆向匹配)

sed适合对整行文本进行处理,如果文本是
column1 column2 column3 column4
...
这种报表格式(bioinformatics里随处可见),awk是最合适的处理工具。

在有了强大的文本处理工具Perl的情况下,为什么还要使用sed和awk?要知道当时Larry创造Perl正是因为sed和awk的局限性。但是当长时间使用Perl处理文本后,你会非常厌倦文件句柄的打开关闭,机械的chomp->split->...print操作,而sed和awk小巧的多。虽然写个perl模板可以解决重复工作的问题,但是sed和awk还可以直接嵌入shell编写的pipeline中,清晰简洁,辅以grep、msort等命令可以高效率的处理很大一部分bioinformatics的工作。而后面的statistics & graph的工作就可以安心的交给R去做了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值