awk处理字符串搜索

搜索匹配

通常在过滤匹配字符串的时候,我们会使用grep命令,而挑选打印出匹配行的某列,又会去结合awk,例如筛选出以下xml中的IP地址,如果采用grep命令过滤出ip行再传递给awk处理就非常raw了,特别是需要多次过滤。

<?xml version="1.0" encoding="utf-8"?>
<rsp>
  <code>200</code>
  <msg>successful</msg>
  <data>
    <ips list="true">
      <item>
        <ip>192.168.1.9</ip>
        <vlan_id>1234</vlan_id>
        <network>public</network>
      </item>
      <item>
        <ip>192.168.1.101</ip>
        <vlan_id>1234</vlan_id>
        <network>public</network>
      </item>
      <item>
        <ip>192.168.1.193</ip>
        <vlan_id>1234</vlan_id>
        <network>public</network>
      </item>
      <item>
        <ip>192.168.1.2</ip>
        <vlan_id>1234</vlan_id>
        <network>public</network>
      </item>
    </ips>
  </data>
</rsp>

awk其实支持几乎所有的grep功能,其搜索字符串非常高效,以下是处理方法:

awk -F "[<>]" '/ip/ && /[0-9]./ {print $3}' ip.xml

说明:

  • /XXXX/ 表示搜索XXXX字符串

  • && 符号表示同时满足两个搜索条件,即字符串中既包含"ip"又包含"数字+.";如果要表示逻辑,就使用||

  • 支持正则表达[]

再举个简单的例子,假如要读取一个配置文件 config.ini 中键值对,并累加计数,即读取出数值是1的时候,加上1等于2再回写配置文件

val=`awk -F= "/^$key=/{print \\$2}" $CONFIG_FILE`

if [ -z "$val" ]; then
    val=1
    echo "$key=$val" >> $CONFIG_FILE
else
    let val+=1
    sed -i 's/^\('$key'=\).*$/\1'$val'/' $CONFIG_FILE
fi

输出大于某值的行

案例文件内容

Medicine,200
Grocery,500
Rent,900
Grocery,800
Medicine,600

输出大于500的行

awk -F, '$2>500' file

输出大于500且包含Medicine的行

awk -F, '/Medicine/ && $2>500' file

输出包含Medicine或值大于600的行

awk -F, '/Medicine/ || $2>600' file

案例(找出磁盘大于某个规格)

上述大于值的过滤在 print line only if number in third field is greater than X [duplicate] 也有一个类似查看磁盘容量大于某个值的查询

lsblk -bio KNAME,TYPE,SIZE,MODEL| awk '/disk/ && $3> 300000000000 || NR==1'

上述 NR==1 是为了保留输出的头部第一行

举例,我的虚拟机

$lsblk -bio KNAME,TYPE,SIZE,MODEL
KNAME TYPE         SIZE MODEL
vda   disk  42949672960
vda1  part  42948607488
vdb   disk 536870912000
vdb1  part 249999392768

则执行

lsblk -bio KNAME,TYPE,SIZE,MODEL | awk '/disk/ && $3> 300000000000 || NR==1'

输出结果

KNAME TYPE         SIZE MODEL
vdb   disk 536870912000

remove lines where a field's value is less than or equal to 3 - sed or awk?

 awk '(NR>1) && ($8 > 2 ) ' foo > bar

对于

忽略大小写

类似grep -i linux file可以匹配LinuxlINux这样的字符串,awk也提供内建的变量表达:

awk '/linux/' IGNORECASE=1 file

计算包含'Unix'字符串的行数量

类似grep -c Unix fileawk提供如下计算方法

awk '/Unix/{x++;}END(print x)' file

这里变量就是"Unix"字符串匹配时候自增量的变量,最后END时候输出变量x就计算出匹配"Unix"的次数。

列出所有包含'AIX'的文件名

类似grep -l AIX file*(这里grep-l不打印匹配字符串,而只是打印包含匹配字符串的文件名),对应awk提供了如下解决方法:

awk '/AIX/{print FILENAME;nextfile}' file*

awk有一个特殊的变量FILNAME包含当前工作的文件的文件名。所以,当匹配时候打印这个变量就是列出了匹配字符串的文件的文件名。nextfileawk的命令表示退出当前文件并开始在新文件上工作。如果不使用nextfile命令,如果这个匹配字符串在文件中出现2次,则文件名会被打印2次。

打印出匹配字符串的行号

grep命令有一个参数-n可以打印出匹配的字符串的行号,例如:

grep -n Unix file

输出显示

1:Unix
7:Unix

对应awk可以使用内建的NR变量来输出行号:

awk '/Unix/{print NR":"$0}' file

搜索文件中的包含'Linux'和'Solaris'

使用grep的参数-E可以启用正则表达式匹配,例如:

grep -E 'Linux|Solaris' file

可以匹配输出如下

Linux
Solaris

对应awk是直接支持正则表达式,无需任何参数:

awk '/Linux|Solaris/' file

反向匹配搜索字符串'Linux'

如果要输出所有包含Linux字符串的行

greo -v Linux file

这里grep命令的-v参数表示inverse

对应awk命令可以使用!表示取反

awk '!/Linux/' file

打印匹配行之后的行

grep命令的参数-A1表示匹配行和之后(After)1行

grep -A1 Linux file

输出是

Linux
Solaris

awk中,提供了getline命令表示从缓存中获取下一行并保存在变量$0中,所以这里在getline命令之后再print就会打印出下一行。当然,在getline命令之前的print就是打印匹配行

awk '/Linux/{print;getline;print}' file

打印匹配行之前的一行

grep命令的参数-B1表示匹配行和之前(Before)1行

grep -B1 Solaris file

输出是

Linux
Solaris

对应在awk中,有一个变量x,当匹配上字符串的时候,这个变量x中存储的是上一行,所以,当打印这个x之后,再使用next指令将指针移动到下一行,此时把$0也就是匹配行复制给x再打印一次,就会打印出当前匹配行:

awk '/Solaris/{print x;print;next}{x=$0;}' file

打印匹配行以及之前1行和之后1行

grep-C1参数可以打印匹配行前后行:

grep -C1 Solaris file

输出是

Linux
Solaris
AIX

对应awk命令则结合前面两个案例如下:

awk '/Solaris/{print x;print;getline;print;next}{x=$0}' file

计算合计

awk '{s+=$1} END {print s}' mydatafile

输出结果但不换行

因为需要结合awk数据结果和特定字符串,所以希望awk输出结果后不换行

awk '{sum+=$3}; END  {printf "%f",sum/NR}' ${file}_${f}_v1.xls >> to-plot-p.xls

print会默认加入换行,如果不希望换行,则使用printf

匹配某列数据

  • 过滤出使用cpu 24的进程,也就是匹配字段4中数字是24的列以后打印匹配行的2列内容

ps -TA -o tid,pid,ppid,psr,cmd | awk '{if($4=='24') print $1" "$5}' | tee cpu_24

参考

Last updated