今天遇到一个数据文件需要处理,文本约1万行,文件每行都是以$字符进行分隔的各种数据列,每行38列。我需要取其中的某一些列,基本都集中在前10列。最初的想法是使用UltraEdit工具进行处理,使用正则表达式查找出了每行中间所需要的内容,无奈UE只支持按行复制,无法单独复制匹配的部分。思来想去,打算到Linux下进行处理,顺便也复习一下常用的一些文本处理命令。Linux命令太强大了,问题果然几需要几步就迎刃而解了。
源文件列格式(以下为示例3行):
0202435718:79849391866393:D02502402$000236677201$00023667720101$D025024028727$D025024028727$D02502402$2015-06-08 13:36:07$0202435718$025301895457$ $0259000$10 $1020 $8727 $126894718$三星手机G9200(铂光金)$000061659$1.0 $D025 $8727$10032424 $20 $订单生效 $2015-06-08 13:36:07$20 $ $ $ $ $8727 $0$ZOR$2015-06-08 13:36:07$R1901001$$5288.0$$N$2015-06-08 13:37:59
6107547155:79849391865963:7089781102$000236677899$00023667789902$7016605928$7089781102$O22106565$2015-06-08 13:40:37$6107547155$6107547155$ $4320501$50 $2700 $9465 $121225083$格兰仕(Galanz)微波炉 P70F23P-G5(SO)$000070689$1.0$D002$0002$10038318$80$订单取消$2015-06-11 18:00:00$13$$$$$$0$ZOR$2015-06-08 13:40:37$R2201001$$399.0$MOBILE|02|01|3.0|10308$N$2015-06-08 13:40:36
6022048888:79849391866085:8091232198$000236678249$00023667824901$8016551693$8091232198$P22142344$2015-06-08 13:39:15$6022048888$6022048888$ $7720200$50 $3100 $9436 $109182779$kidsme亲亲我 摇铃组合牙刷 130065AE$000190BXL$1.0$D069$0001$10079703$70$订单拒收退$2015-06-10 18:00:00$14$$$$$K772020601$0$ZOR$2015-06-08 13:39:15$R9003035$$8.9$WAP$N$
我需要的内容为:
以$符为分隔,第3,5,6,7,8列,并且将第7列日期格式由yyyy-mm-dd hh:mm:ss修改为yyyymmddhhmmss,最终各列以,分隔
通过分析处理步骤,可知大致处理步骤为三步:
1.分割文件并取出需要的列;——使用cut命令
2.查找并替换日期格式;——使用sed命令
3.查找列分隔符$并替换为, ——使用sed命令
cut命令的用法:
[root@www ~]# cut -d '分隔字符' -f fields <==用于有特定分隔字符
[root@www ~]# cut -c 字符区间 <==用于排列整齐的讯息
选项与参数:
-d :后面接分隔字符。与 -f 一起使用;
-f :依据 -d 癿分隔字符将一段讯息分割成为数段,用 -f 取出第几段的意思;
-c :以字符 (characters) 的单位取出固定字符区间;
源文件文件名为source.txt,并上传至服务器:
[loguser@svcpreapp01 cut]$ ll
total 3524
drwxrwxr-x 2 loguser loguser 4096 Dec 29 16:31 ok
-rw-r--r-- 1 loguser loguser 3598763 Dec 29 16:30 source.txt
因文件行数较多,我们这里把结果文件输出到result.txt文件中,那么这里第一步我们需要使用的命令为:
cut -d '$' -f 3,5,6,7,8 source.txt > result.txt
[loguser@svcpreapp01 cut]$ cut -d '$' -f 3,5,6,7,8 source.txt > result.txt
[loguser@svcpreapp01 cut]$ ll
total 4208
drwxrwxr-x 2 loguser loguser 4096 Dec 29 16:31 ok
-rw-rw-r-- 1 loguser loguser 694021 Dec 29 16:33 result.txt
-rw-r--r-- 1 loguser loguser 3598763 Dec 29 16:30 source.txt
[loguser@svcpreapp01 cut]$
可以查看一下前10行的内容,看文件格式是否符合要求:head -n 10 result.txt
[loguser@svcpreapp01 cut]$ head -n 10 result.txt
00023667720101$D025024028727$D02502402$2015-06-08 13:36:07$0202435718
00023667789902$7089781102$O22106565$2015-06-08 13:40:37$6107547155
00023667824901$8091232198$P22142344$2015-06-08 13:39:15$6022048888
00023667938001$D002291287852$D00229128$2015-06-08 13:42:32$0101855315
00023667967601$D003101939312$D00310193$2015-06-08 13:44:07$6107554066
00023668329501$D000577509545$D00057750$2015-06-08 13:52:58$6016175500
00023668400101$D021609489358$D02160948$2015-06-08 13:54:58$6107422418
00023668457901$D004719687172$D00471968$2015-06-08 13:56:40$6100439132
00023668851801$4089771406$I22114577$2015-06-08 14:14:33$2214456603
00023668867701$1097223690$C23934507$2015-06-08 14:04:48$6107463339
[loguser@svcpreapp01 cut]$
如上,已经通过文件分割得到需要的文件
Linux下批量替换多个文件中的字符串的简单方法。用sed命令可以批量替换多个文件中的字符串。
命令如下:sed -i "s/原字符串/新字符串/g" `grep 原字符串 -rl 所在目录`
例如:我要把 charset=gb2312 替换为 charset=UTF-8,执行命令:sed -i "s/charset=gb2312/charset=UTF-8/g" `grep charset=gb2312 -rl /www`; 即可。
解释一下:
-i 表示inplace edit,就地修改文件
-r 表示搜索子目录
-l 表示输出匹配的文件名
这个命令组合很强大,要注意备份文件。
替换命令为,其中查找的字符支持正则表达式匹配:sed -i "s/[- :]//g" result.txt
[loguser@svcpreapp01 cut]$ cp result.txt result-bak.txt
[loguser@svcpreapp01 cut]$ ll
total 4892
drwxrwxr-x 2 loguser loguser 4096 Dec 29 16:31 ok
-rw-rw-r-- 1 loguser loguser 694021 Dec 29 16:42 result-bak.txt
-rw-rw-r-- 1 loguser loguser 694021 Dec 29 16:33 result.txt
-rw-r--r-- 1 loguser loguser 3598763 Dec 29 16:30 source.txt
[loguser@svcpreapp01 cut]$ sed -i "s/[- :]//g" result.txt
[loguser@svcpreapp01 cut]$ head -n 10 result.txt
00023667720101$D025024028727$D02502402$20150608133607$0202435718
00023667789902$7089781102$O22106565$20150608134037$6107547155
00023667824901$8091232198$P22142344$20150608133915$6022048888
00023667938001$D002291287852$D00229128$20150608134232$0101855315
00023667967601$D003101939312$D00310193$20150608134407$6107554066
00023668329501$D000577509545$D00057750$20150608135258$6016175500
00023668400101$D021609489358$D02160948$20150608135458$6107422418
00023668457901$D004719687172$D00471968$20150608135640$6100439132
00023668851801$4089771406$I22114577$20150608141433$2214456603
00023668867701$1097223690$C23934507$20150608140448$6107463339
[loguser@svcpreapp01 cut]$
如上,已替换成功。
$字符在正则匹配的时候会认为是一行的结尾,因此需要使用转义,同时要注意使用单引号,如下,文件处理完成后即可得到想要的文件了:
[loguser@svcpreapp01 cut]$ sed -i 's/\$/,/g' result.txt
[loguser@svcpreapp01 cut]$ head -n 10 result.txt
00023667720101,D025024028727,D02502402,20150608133607,0202435718
00023667789902,7089781102,O22106565,20150608134037,6107547155
00023667824901,8091232198,P22142344,20150608133915,6022048888
00023667938001,D002291287852,D00229128,20150608134232,0101855315
00023667967601,D003101939312,D00310193,20150608134407,6107554066
00023668329501,D000577509545,D00057750,20150608135258,6016175500
00023668400101,D021609489358,D02160948,20150608135458,6107422418
00023668457901,D004719687172,D00471968,20150608135640,6100439132
00023668851801,4089771406,I22114577,20150608141433,2214456603
00023668867701,1097223690,C23934507,20150608140448,6107463339
[loguser@svcpreapp01 cut]$
以下部分为我在网上看到的一些总结,附在这里补充说明,主要是想强调单引号和双引号的区别:
Sed里使用变量的问题,网上有人总结了四种方案:
1. eval sed 's/$a/$b/' filename
2. sed "s/$a/$b/" filename
3. .sed 's/'$a'/'$b'/' filename
4. .sed s/$a/$b/ filename
我比较喜欢第二种,也就是:Sed后面的表达式一般用单引号引起来('),当需要使用变量时就换用双引号(")。
关于单双引号的区别:
单引号:shell处理命令时,对其中的内容不做任何处理。即此时是引号内的内容是sed命令所定义的格式。
双引号:shell处理命令时,要对其中的内容进行算术扩展。如果想让shell扩展后得到sed命令所要的格式,使用命令:sed -n"/\\\\$/p" haha,扩展后得到的结果即\\$.
总结一下,其实这里总共使用了三行命令就解决了问题,而且处理速度也比较快:
[loguser@svcpreapp01 cut]$ cut -d '$' -f 3,5,6,7,8 source.txt > result.txt
[loguser@svcpreapp01 cut]$ sed -i "s/[- :]//g" result.txt
[loguser@svcpreapp01 cut]$ sed -i 's/\$/,/g' result.txt