目录
在”a b c d”的b后面插入3个字段e f g。
- [root@centos111 test]# echo a b c d|awk '{$3="e f g "$3}1'
- a b e f g c d
移除每行的前缀、后缀空白,并将各部分左对齐。
文本内容:
- PLAINTEXT
- aaaa bbb ccc
- bbb aaa ccc
- ddd fff eee gg hh ii jj
awk实现:
- awk 'BEGIN{OFS="\t"}{$1=$1;print}' test.txt
- PLAINTEXT
- aaaa bbb ccc
- bbb aaa ccc
- ddd fff eee gg hh ii jj
从ifconfig命令的结果中筛选出除了lo网卡外的所有IPv4地址。
方法1:
- [root@centos111 test]# ifconfig | awk '/inet / && !($2 ~ /^127/){print $2}'
- 192.168.159.200
方法2:
- [root@centos111 test]# ifconfig | awk 'BEGIN{RS=""}!/lo/{print $6}'
- 192.168.159.200
- //这里是利用指定输入时的换行符为空格,然后取到了第六个字符,这个字符就是我们需要的ip
方法3:
- [root@centos111 test]# ifconfig | awk 'BEGIN{RS="";FS="\n"}!/lo/{$0=$2;FS=" ";$0=$0;print $2}'
- 192.168.159.200
文本内容:
- [base]
- name=os_repo
- baseurl=https://xxx/centos/$releasever/os/$basearch
- gpgcheck=0
-
- enable=1
-
- [mysql]
- name=mysql_repo
- baseurl=https://xxx/mysql-repo/yum/mysql-5.7-community/el/$releasever/$basearch
-
- gpgcheck=0
- enable=1
-
- [epel]
- name=epel_repo
- baseurl=https://xxx/epel/$releasever/$basearch
- gpgcheck=0
- enable=1
- [percona]
- name=percona_repo
- baseurl = https://xxx/percona/release/$releasever/RPMS/$basearch
- enabled = 1
- gpgcheck = 0
awk实现:
script.sh:
- BEGIN{RS=""}
- /\[mysql\]/{
- print;
- while( (getline)>0 ){
- if(/\[.*\]/){exit}
- print
- }
- }
- [root@centos111 test]# awk -f script.sh test.txt
- [mysql]
- name=mysql_repo
- baseurl=https://xxx/mysql-repo/yum/mysql-5.7-community/el/$releasever/$basearch
- gpgcheck=0
- enable=1
文本内容:
- 2019-01-13_12:00_index?uid=123
- 2019-01-13_13:00_index?uid=123
- 2019-01-13_14:00_index?uid=333
- 2019-01-13_15:00_index?uid=9710
- 2019-01-14_12:00_index?uid=123
- 2019-01-14_13:00_index?uid=123
- 2019-01-15_14:00_index?uid=333
- 2019-01-16_15:00_index?uid=9710
awk实现:
- awk -F "?" '!arr[$2]++{print}' test.txt
- 2019-01-13_12:00_index?uid=123
- 2019-01-13_14:00_index?uid=333
- 2019-01-13_15:00_index?uid=9710
文本内容:
- portmapper
- portmapper
- portmapper
- portmapper
- portmapper
- portmapper
- status
- status
- mountd
- mountd
- mountd
- mountd
- mountd
- mountd
- nfs
- nfs
- nfs_acl
- nfs
- nfs
- nfs_acl
- nlockmgr
- nlockmgr
- nlockmgr
- nlockmgr
- nlockmgr
awk实现:
- [root@centos111 test]# awk '{arr[$0]++}END{for(i in arr){print arr[i],i}}' test.txt
- 4 nfs
- 2 status
- 5 nlockmgr
- 6 portmapper
- 2 nfs_acl
- 6 mountd
文本内容:
- netstat -antup
- Active Internet connections (servers and established)
- Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
- tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 1105/php-fpm: maste
- tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 719/rpcbind
- tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN 1583/dnsmasq
- tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1103/sshd
- tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 1101/cupsd
- tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN 1667/sshd: root@pts
- tcp 0 36 192.168.159.200:22 192.168.159.1:58404 ESTABLISHED 1667/sshd: root@pts
- tcp6 0 0 :::3306 :::* LISTEN 1377/mysqld
- tcp6 0 0 :::111 :::* LISTEN 719/rpcbind
- tcp6 0 0 :::22 :::* LISTEN 1103/sshd
- tcp6 0 0 ::1:631 :::* LISTEN 1101/cupsd
- tcp6 0 0 ::1:6010 :::* LISTEN 1667/sshd: root@pts
- tcp6 0 0 :::33060 :::* LISTEN 1377/mysqld
- udp 0 0 192.168.122.1:53 0.0.0.0:* 1583/dnsmasq
- udp 0 0 0.0.0.0:67 0.0.0.0:* 1583/dnsmasq
- udp 0 0 0.0.0.0:111 0.0.0.0:* 719/rpcbind
- udp 0 0 0.0.0.0:5353 0.0.0.0:* 728/avahi-daemon: r
- udp 0 0 0.0.0.0:42752 0.0.0.0:* 728/avahi-daemon: r
- udp 0 0 127.0.0.1:323 0.0.0.0:* 743/chronyd
- udp 0 0 0.0.0.0:881 0.0.0.0:* 719/rpcbind
- udp6 0 0 :::111 :::* 719/rpcbind
- udp6 0 0 ::1:323 :::* 743/chronyd
- udp6 0 0 :::881 :::* 719/rpcbind
awk实现:
- [root@centos111 test]# netstat -antup | awk '/^tcp/{arr[$6]++}END{for(i in arr){print arr[i],i}}'
- 12 LISTEN
- 1 ESTABLISHED
日志数据:
- 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 301 169
- 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 200 169
- 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 301 169
- 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 301 169
- 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 200 169 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 404 169
- 111.202.100.141 - - [2019-11-07T03:11:02+08:00] "GET /robots.txt HTTP/1.1" 200 169
统计非200状态码的IP,并取次数最多的前10个IP。
awk实现:
- [root@centos111 test]# awk '$8!=200{arr[i]++}END{for(i in arr)print arr[i],i}' log.txt |sort -klnr | head
- 3
url 访问IP 访问时间 访问人
文件内容:
- a.com.cn|202.109.134.23|2015-11-20 20:34:43|guest
- b.com.cn|202.109.134.23|2015-11-20 20:34:48|guest
- c.com.cn|202.109.134.24|2015-11-20 20:34:48|guest
- a.com.cn|202.109.134.23|2015-11-20 20:34:43|guest
- a.com.cn|202.109.134.24|2015-11-20 20:34:43|guest
- b.com.cn|202.109.134.25|2015-11-20 20:34:48|guest
需求:统计每个URL的独立访问IP有多少个(去重),并且要为每个URL保存一个对应的文件,得到的结果类似:
a.com.cn 2
b.com.cn 2
c.com.cn 1
并且有三个对应的文件:
a.com.cn.txt b.com.cn.txt c.com.cn.txt
awk实现:
script.sh:
- [root@centos111 test]# cat script.sh
- BEGIN{
- FS="|"
- }
- !arr[$1,$2]++{
- arr1[$1]++
- }
- END {
- for (i in arr1) {
- print i,arr1[i] > (i".txt")
- }
- }
[root@centos111 test]# awk -f script.sh test.txt
可以查看一下生成的文件:
- [root@centos111 test]# cat a.com.cn.txt
- a.com.cn 2
- [root@centos111 test]# cat b.com.cn.txt
- b.com.cn 2
- [root@centos111 test]# cat c.com.cn.txt
- c.com.cn 1
文本内容:
- ID name gender age email phone
- 1 Bob male 28 abc@qq.com 18023394012
- 2 Alice female 24 def@gmail.com 18084925203
- 3 Tony male 21 17048792503
- 4 Kevin male 21 bbb@189.com 17023929033
- 5 Alex male 18 ccc@xyz.com 18185904230
- 6 Andy female ddd@139.com 18923902352
- 7 Jerry female 25 exdsa@189.com 18785234906
- 8 Peter male 20 bax@qq.com 17729348758
- 9 Steven 23 bc@sohu.com 15947893212
- 10 Bruce female 27 bcbd@139.com 13942943905
当字段缺失时,直接使用FS划分字段来处理会非常棘手。gawk为了解决这种特殊需求,提供了FIELDWIDTHS变量。
FIELDWIDTH可以按照字符数量划分字段。
awk实现:
[root@centos111 test]# awk '{print $4}' FIELDWIDTHS='2 2:6 2:3 2:13 2:11' test.txt
下面是CSV文件中的一行,该CSV文件以逗号分隔各个字段。
Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,12345-6789,USA
需求:取得第三个字段”1234 A Pretty Street, NE”。
当字段中包含了字段分隔符时,直接使用FS划分字段来处理会非常棘手。gawk为了解决这种特殊需求,提供了FPAT变量。
FPAT可以收集正则匹配的结果,并将它们保存在各个字段中。(就像grep匹配成功的部分会加颜色显示,而使用FPAT划分字段,则是将匹配成功的部分保存在字段$1 $2 $3...中)。
awk实现:
- [root@centos111 test]# echo 'Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,12345-6789,USA' |\
- > awk 'BEGIN{FPAT="[^,]+|\".*\""}{print $1,$3}'
- Robbins "1234 A Pretty Street, NE"
文本内容:
- 16 001agdcdafasd
- 16 002agdcxxxxxx
- 23 001adfadfahoh
- 23 001fsdadggggg
得到:
- 16 001
- 16 002
- 23 001
- 23 002
awk实现:
- [root@centos111 test]# awk '{print $1,substr($2,1,3)}' test.txt
- 16 001
- 16 002
- 23 001
- 23 001
文件内容:
- name age
- alice 21
- ryan 30
转换得到:
- name alice ryan
- age 21 30
awk实现:
script.sh:
-
- {
- for(i=1;i<=NF;i++){
- if(!(i in arr)){
- arr[i]=$i
- } else {
- arr[i]=arr[i]" "$i
- }
- }
- }
- END{
- for(i=1;i<=NF;i++){
- print arr[i]
- }
- }
- [root@centos111 test]# awk -f script.sh test.txt
- me alice ryan
- age 21 30
文件内容:
- 74683 1001
- 74683 1002
- 74683 1011
- 74684 1000
- 74684 1001
- 74684 1002
- 74685 1001
- 74685 1011
- 74686 1000
- ....
- 100085 1000
- 100085 1001
文件就两列,希望处理成
- 74683 1001 1002 1011
- 74684 1000 1001 1002
- ...
就是只要第一列数字相同, 就把他们的第二列放一行上,中间空格分开
awk实现:
script.sh:
- {
- if($1 in arr){
- arr[$1] = arr[$1]" "$2
- } else {
- arr[$1] = $2
- }
-
- }
-
- END{
- for(i in arr){
- printf "%s %s\n",i,arr[i]
- }
- }
- [root@centos111 test]# awk -f script.sh test.txt
- 74686 1000
- ....
- 100085 1000 1001
- 74683 1001 1002 1011
- 74684 1000 1001 1002
- 74685 1001 1011
grep/sed/awk用正则去筛选日志时,如果要精确到小时、分钟、秒,则非常难以实现。
但是awk提供了mktime()函数,它可以将时间转换成epoch时间值。
- # 2019-11-10 03:42:40转换成epoch
- $ awk 'BEGIN{print mktime("2019 11 10 03 42 40")}'
- 1573328560
借此,可以取得日志中的时间字符串部分,再将它们的年、月、日、时、分、秒都取出来,然后放入mktime()构建成对应的epoch值。因为epoch值是数值,所以可以比较大小,从而决定时间的大小。
下面strptime1()实现的是将2019-11-10T03:42:40+08:00格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志。
下面strptime2()实现的是将10/Nov/2019:23:53:44+08:00格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志。、
strtime1():
- [root@centos111 test]# cat script.sh
- BEGIN {
- which_time = mktime("2023 11 18 10 14 30")
- }
- {
- match($0,"^.*\\[(.*)\\].*",arr)
- tmp_time = strptime1(arr[1])
- if(tmp_time > which_time){print}
- }
- function strptime(str ,arr,Y,M,D,H,m,S){
- patsplit(str,arr,"[0-9]{1,4}")
- Y=arr[1]
- M=arr[2]
- D=arr[3]
- H=arr[4]
- m=arr[5]
- S=arr[6]
- return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))}
strtime2():
- PLAINTEXT
- BEGIN{
- # 要筛选什么时间的日志,将其时间构建成epoch值
- which_time = mktime("2023 11 18 10 14 30")
- }
-
- {
- # 取出日志中的日期时间字符串部分
- match($0,"^.*\\[(.*)\\].*",arr)
-
- # 将日期时间字符串转换为epoch值
- tmp_time = strptime2(arr[1])
-
- # 通过比较epoch值来比较时间大小
- if(tmp_time > which_time){
- print
- }
- }
-
- # 构建的时间字符串格式为:"10/Nov/2019:23:53:44+08:00"
- function strptime2(str ,dt_str,arr,Y,M,D,H,m,S) {
- dt_str = gensub("[/:+]"," ","g",str)
- # dt_sr = "10 Nov 2023 11 18 10 14 30"
- split(dt_str,arr," ")
- Y=arr[3]
- M=mon_map(arr[2])
- D=arr[1]
- H=arr[4]
- m=arr[5]
- S=arr[6]
- return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
- }
-
- function mon_map(str ,mons){
- mons["Jan"]=1
- mons["Feb"]=2
- mons["Mar"]=3
- mons["Apr"]=4
- mons["May"]=5
- mons["Jun"]=6
- mons["Jul"]=7
- mons["Aug"]=8
- mons["Sep"]=9
- mons["Oct"]=10
- mons["Nov"]=11
- mons["Dec"]=12
- return mons[str]
- }
/**/中间的注释示例数据:
- /*AAAAAAAAAA*/
- 1111
- 222
-
- /*aaaaaaaaa*/
- 32323
- 12341234
- 12134 /*bbbbbbbbbb*/ 132412
-
- 14534122
- /*
- cccccccccc
- */
- xxxxxx /*ddddddddddd
- cccccccccc
- eeeeeee
- */ yyyyyyyy
- 5642341
script:
- /\/\*/{
-
- if (/\*\//){
- print gensub("(.*)/\\*.*\\*/(.*)","\\1\\2","g",$0)
- }
- else
- {
- print gensub("(.*)/\\*.*","\\1","g",$0)
- while ( (getline) >0 ){
- if(/\*\//){
- print gensub(".*\\*/(.*)","\\1","g",$0)
- }
- }
- }
- }
- !/\/\*/{print}
awk实现:
- [root@centos111 test]# awk -f script.sh test.txt
-
- 1111
- 222
-
-
- 32323
- 12341234
- 12134 132412
-
- 14534122
-
-
- yyyyyyyy
- 5642341
从如下类型的文件中,找出false段的前一段为i-order的段,同时输出这两段。
- 2019-09-12 07:16:27 [-][
- 'data' => [
- 'http://192.168.100.20:2800/api/payment/i-order',
- ],
- ]
- 2019-09-12 07:16:27 [-][
- 'data' => [
- false,
- ],
- ]
- 2019-09-21 07:16:27 [-][
- 'data' => [
- 'http://192.168.100.20:2800/api/payment/i-order',
- ],
- ]
- 2019-09-21 07:16:27 [-][
- 'data' => [
- 'http://192.168.100.20:2800/api/payment/i-user',
- ],
- ]
- 2019-09-17 18:34:37 [-][
- 'data' => [
- false,
- ],
- ]
- PLAINTEXT
- BEGIN{
- RS="]\n"
- ORS=RS
- }
- {
- if(/false/ && prev ~ /i-order/){
- print tmp
- print
- }
- tmp=$0
- }
递归正则搜索:
grep -Pz '(?s)\d+((?!2019).)*i-order(?1)+\d+(?1)+false(?1)+'
有两个文件file1和file2,这两个文件格式都是一样的。
需求:先把文件2的第五列删除,然后用文件2的第一列减去文件一的第一列,把所得结果对应的贴到原来第五列的位置,请问这个脚本该怎么编写?
- file1:
- 50.481 64.634 40.573 1.00 0.00
- 51.877 65.004 40.226 1.00 0.00
- 52.258 64.681 39.113 1.00 0.00
- 52.418 65.846 40.925 1.00 0.00
- 49.515 65.641 40.554 1.00 0.00
- 49.802 66.666 40.358 1.00 0.00
- 48.176 65.344 40.766 1.00 0.00
- 47.428 66.127 40.732 1.00 0.00
- 51.087 62.165 40.940 1.00 0.00
- 52.289 62.334 40.897 1.00 0.00
- file2:
- 48.420 62.001 41.252 1.00 0.00
- 45.555 61.598 41.361 1.00 0.00
- 45.815 61.402 40.325 1.00 0.00
- 44.873 60.641 42.111 1.00 0.00
- 44.617 59.688 41.648 1.00 0.00
- 44.500 60.911 43.433 1.00 0.00
- 43.691 59.887 44.228 1.00 0.00
- 43.980 58.629 43.859 1.00 0.00
- 42.372 60.069 44.032 1.00 0.00
- 43.914 59.977 45.551 1.00 0.00
awk实现:
方法1:
script:
- awk '{
- f1 =$1
- if( (getline < "file2") >=0 ){
- $5 = $1-f1
- print $0
- }
- }' file1
- [root@centos111 test]# ./script.sh
- 48.420 62.001 41.252 1.00 -2.061
- 45.555 61.598 41.361 1.00 -6.322
- 45.815 61.402 40.325 1.00 -6.443
- 44.873 60.641 42.111 1.00 -7.545
- 44.617 59.688 41.648 1.00 -4.898
- 44.500 60.911 43.433 1.00 -5.302
- 43.691 59.887 44.228 1.00 -4.485
- 43.980 58.629 43.859 1.00 -3.448
- 42.372 60.069 44.032 1.00 -8.715
- 43.914 59.977 45.551 1.00 -8.375
方法2:
script:
- awk '
- NR==FNR{arr[FNR]=$1}
- NR!=FNR{$5=$1-arr[FNR];print}
- ' file1 file2
- [root@centos111 test]# ./script.sh
- 48.420 62.001 41.252 1.00 -2.061
- 45.555 61.598 41.361 1.00 -6.322
- 45.815 61.402 40.325 1.00 -6.443
- 44.873 60.641 42.111 1.00 -7.545
- 44.617 59.688 41.648 1.00 -4.898
- 44.500 60.911 43.433 1.00 -5.302
- 43.691 59.887 44.228 1.00 -4.485
- 43.980 58.629 43.859 1.00 -3.448
- 42.372 60.069 44.032 1.00 -8.715
- 43.914 59.977 45.551 1.00 -8.375
如下内容,第一个字段是IP,第二个字段是每个访问的uri。
- 1.1.1.1 /index1.html
- 1.1.1.1 /index1.html
- 1.1.1.1 /index2.html
- 1.1.1.1 /index2.html
- 1.1.1.1 /index2.html
- 1.1.1.1 /index3.html
- 1.1.1.2 /index1.html
- 1.1.1.2 /index2.html
- 1.1.1.2 /index2.html
- 1.1.1.3 /index1.html
- 1.1.1.3 /index1.html
- 1.1.1.3 /index2.html
- 1.1.1.3 /index2.html
- 1.1.1.3 /index2.html
- 1.1.1.3 /index3.html
- 1.1.1.3 /index3.html
- 1.1.1.4 /index2.html
- 1.1.1.4 /index2.html
要求统计出每个ip访问的总次数,以及每个ip所访问的uri的次数。
期望的输出结果:
- 1.1.1.1 6 /index3.html 1
- 1.1.1.1 6 /index2.html 3
- 1.1.1.1 6 /index1.html 2
- 1.1.1.2 3 /index2.html 2
- 1.1.1.2 3 /index1.html 1
- 1.1.1.3 7 /index3.html 2
- 1.1.1.3 7 /index2.html 3
- 1.1.1.3 7 /index1.html 2
- 1.1.1.4 2 /index2.html 2
方法1,使用子数组。
script代码:
- {
- a[$1][$2]++
- }
- END{
- # 遍历数组,统计每个ip的访问总数
- for(ip in a){
- for(uri in a[ip]){
- b[ip] += a[ip][uri]
- }
- }
-
- # 再次遍历
- for(ip in a){
- for(uri in a[ip]){
- print ip, b[ip], uri, a[ip][uri]
- }
- }
- }
- [root@centos111 test]# ./script.sh
- 1.1.1.1 6 /index3.html 1
- 1.1.1.1 6 /index2.html 3
- 1.1.1.1 6 /index1.html 2
- 1.1.1.2 3 /index2.html 2
- 1.1.1.2 3 /index1.html 1
- 1.1.1.3 7 /index3.html 2
- 1.1.1.3 7 /index2.html 3
- 1.1.1.3 7 /index1.html 2
- 1.1.1.4 2 /index2.html 2
方法2,使用复合索引的数组。
script代码:
- {
- a[$1]++
- b[$1"_"$2]++
- }
- END{
- for(i in b){
- split(i,c,"_");
- print c[1],a[c[1]],c[2],b[i]
- }
- }
- [root@centos111 test]# ./script.sh
- 1.1.1.2 3 /index2.html 2
- 1.1.1.4 2 /index2.html 2
- 1.1.1.2 3 /index1.html 1
- 1.1.1.1 6 /index3.html 1
- 1.1.1.3 7 /index3.html 2
- 1.1.1.1 6 /index2.html 3
- 1.1.1.3 7 /index2.html 3
- 1.1.1.1 6 /index1.html 2
- 1.1.1.3 7 /index1.html 2
文件内容如下:
- { "ent_id" : MinKey, "_id" : MinKey } -->> {
- "ent_id" : NumberLong("aaaaa"),
- "_id" : ObjectId("bbbbb")
- } on : shard04 Timestamp(685, 0)
- {
- "ent_id" : NumberLong("ccccc"),
- "_id" : ObjectId("ddddd")
- } -->> {
- "ent_id" : NumberLong("eeeee"),
- "_id" : ObjectId("fffff")
- } on : shard04 Timestamp(331, 1)
- {
- "ent_id" : NumberLong("ggggg"),
- "_id" : ObjectId("hhhhh")
- } -->> {
- "ent_id" : NumberLong("iiiii"),
- "_id" : ObjectId("jjjjj")
- } on : shard04 Timestamp(680, 0)
期望结果:
- MinKey,MinKey,NumberLong("aaaaa"),ObjectId("bbbbb"),shard04
- NumberLong("ccccc"),ObjectId("ddddd"),NumberLong("eeeee"),ObjectId("fffff"),shard04
- NumberLong("ggggg"),ObjectId("hhhhh"),NumberLong("iiiii"),ObjectId("jjjjj"),shard04
script:
- BEGIN{
- # 以Timestamp...为输入记录分隔符,一次读取一段
- RS=" Timestamp\\([0-9]+, [0-9]\\)"
- }
- {
- # 将一段中所有冒号后的内容保存到数组
- patsplit($0,arr,": ([0-9a-zA-Z\"\\(\\)])+")
- for(i in arr){
- # 移除冒号,并使用逗号分隔串联各元素
- str = str gensub(": ","","g",arr[i])","
- }
- # 移除尾部逗号
- print(substr(str,1,length(str)-1))
- str=""
-
- }
- [root@centos111 test]# awk -f script.sh test.txt
- ObjectId("bbbbb"),shard04,MinKey,MinKey,NumberLong("aaaaa")
- ObjectId("fffff"),shard04,NumberLong("ccccc"),ObjectId("ddddd"),NumberLong("eeeee")
- ObjectId("jjjjj"),shard04,NumberLong("ggggg"),ObjectId("hhhhh"),NumberLong("iiiii")
使用Perl或Ruby则更简单:
- perl -0nE 'BEGIN{$,=","}say $& =~ /: \K[^\s,]+/g while /{.*?} on : \S+/sg' test.log
- ruby -ne 'BEGIN{$/=nil};$_.scan(/{.*?} on : \S+/m){|s|puts s.scan(/: \K[^\s,]+/).join(",")}' test.log
到这里Linux三剑客就全部介绍完毕了,最后再总结一下这三种:
grep基本是以行为单位处理文本的; 而awk可以做更细分的处理,通过指定分隔符将一行(一条记录)划分为多个字段,以字段为单位处理文本。
grep功能简单,就是一个简单的正则表达式的匹配。
grep可以理解为主要作用是在一个文件中查找过滤需要的内容。
sed是一个非交互性文本流编辑器。它编辑文件或标准输入导出的文本拷贝。sed编辑器按照一次处理 一行的方式来处理文件(或者输入)并把输出送到屏幕上。你可以在vi和ex/ed编辑器里识别他的命令。sed把当前正在处理的行保存在一个临时缓存里,这个缓存叫做模式空间。一但sed完成了对模式空间里的行的处理(即对该行执行sed命令),就把模式空间的行送到屏幕上(除非该命令要删除该行活禁止打印)。处理完该行之后,从模式空间里删除它,然后把下一行读入模式空间,进行处理,并显示。当输入文件的最后一行处理完后,sed终止。通过把每一行存在一个临时缓存里并编辑该行,初始文件不会被修改或被破坏。
AWK的功能是什么?
与sed和grep很相似,awk是一种样式扫描与处理工具,但其功能却大大强于sed和grep。
awk中支持C语法,可以有分支条件判断、循环语句等,相当于一个小型编程语言。
awk功能比较多是一个编程语言了。
awk提供了极其强大的功能:它几乎可以完成grep和sed所能完成的全部工作,同时,它还可以可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。实际上,awk的确拥有自己的语言:awk程序设计语言,awk的三位创建者已将它正式定义为:样式扫描和处理语言。 使用awk的第一个理由是基于文本的样式扫描和处理是我们经常做的工作,awk所做的工作有些象数据库,但与数据库不同的是,它处理的是文本文件,这些文件没有专门的存储格式,普通的人们就能编辑、阅读、理解和处理它们。而数据库文件往往具有特殊的存储格式,这使得它们必须用数据库处理程序来处理它们。既然这种类似于数据库的处理工作我们经常会遇到,我们就应当找到处理它们的简便易行的方法,UNIX有很多这方面的工具,例如sed 、grep、sort以及find等等,awk是其中十分优秀的一种。
使用awk的第二个理由是awk是一个简单的工具,当然这是相对于其强大的功能来说的。的确,UNIX有许多优秀的工具,例如UNIX天然的开发工具C语言及其延续C++就非常的优秀。但相对于它们来说,awk完成同样的功能要方便和简捷得多。这首先是因为awk提供了适应多种需要的解决方案:从解决简单问题的awk命令行到复杂而精巧的awk程序设计语言,这样做的好处是,你可以不必用复杂的方法去解决本来很简单的问题。例如,你可以用一个命令行解决简单的问题,而C不行,即使一个再简单的程序,C语言也必须经过编写、编译的全过程。其次,awk本身是解释执行的,这就使得awk程序不必经过编译的过程,同时,这也使得它与shell script程序能够很好的契合。最后,awk本身较C语言简单,虽然awk吸收了C语言很多优秀的成分,熟悉C语言会对学习awk有很大的帮助,但awk本身不须要会使用C语言——一种功能强大但需要大量时间学习才能掌握其技巧的开发工具。
使用awk的第三个理由是awk是一个容易获得的工具。与C和C++语言不同,awk只有一个文件(/bin/awk),而且几乎每个版本的UNIX都提供各自版本的awk,你完全不必费心去想如何获得awk。但C语言却不是这样,虽然C语言是UNIX天然的开发工具,但这个开发工具却是单独发行的,换言之,你必须为你的UNIX版本的C语言开发工具单独付费(当然使用D版者除外),获得并安装它,然后你才可以使用它。
基于以上理由,再加上awk强大的功能,我们有理由说,如果你要处理与文本样式扫描相关的工作,awk应该是你的第一选择。在这里有一个可遵循的一般原则:如果你用普通的shell工具或shell script有困难的话,试试awk,如果awk仍不能解决问题,则便用C语言,如果C语言仍然失败,则移至C++。