使用多个定界符的 AWK 正则表达式拆分函数

AWK regex split function using multiple delimiters

我正在尝试使用 Awk 的拆分函数将输入拆分为三个字段,以便将值用作字段 [1]、字段[2]、字段[3]。我正在尝试提取第一个值:所有内容(包括冒号),然后是第一个制表符 (\t)(十六进制)之前的所有内容,然后最后一个字段将包括其他所有内容。

我尝试了多个正则表达式,最接近解决这个问题的是:

echo -e "ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf" \
| awk '{split([=10=],field,/([:])([ ])|([\t])/); \
print "length of field:" length(field);for (x in field) print field[x]}'

但结果不包括冒号——我不确定我写的正则表达式是否正确:

length of field:3
ffffffff81000000
48 8d 25 51 3f 60 01
leaq asdf asdf asdf

提前致谢。

您的正则表达式可以简化为:

split([=10=],field,/: |\t/)

但是不包含冒号字符的结果是一样的 因为分隔符模式不包含在拆分结果中。

如果您想使用复杂的模式,例如 a whitespace preceded by a colon 作为 split 函数中的分隔符,您需要使用 PCRE 这不是 awk.

支持

这里有一个 python 的例子:

#!/usr/bin/python

import re

s = "ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf"
print(re.split(r'(?<=:) |\t', s))

输出:

['ffffffff81000000:', '48 8d 25 51 3f 60 01', 'leaq asdf asdf asdf']

您会看到冒号包含在结果中。

使用gnu-awkRS(用于记录分隔符)变量:

s=$'ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf'
awk -v RS='^\S+|[^\t:]+' '{gsub(/^\s*|\s*$/, "", RT); print RT}' <<< "$s"

ffffffff81000000:
48 8d 25 51 3f 60 01
leaq asdf asdf asdf

解释:

  • RS='^\S+|[^\t:]+':将 RS 设置为开头的 1+ 个非空白字符或 1+ 个非制表符、非冒号字符
  • gsub(/^\s*|\s*$/, "", RT)RT 变量中删除了开头或结尾的空格,该变量因 RS
  • 而被填充
  • print RTprintsRT`变量

如果您还想打印字段的长度,请使用:

awk -v RS='^\S+|[^\t:]+' '{gsub(/^\s*|\s*$/, "", RT); print RT} END {print "length of field:", NR}' <<< "$s"

ffffffff81000000:
48 8d 25 51 3f 60 01
leaq asdf asdf asdf
length of field: 3

如果您没有 gnu-awk 那么这里有一个 POSIX awk 解决方案:

awk '{
   while (match([=12=], /^[^[:blank:]]+|[^\t:]+/)) {
      print substr([=12=], RSTART, RLENGTH)
      [=12=] = substr([=12=], RSTART+RLENGTH)
   }
}' <<< "$s"

ffffffff81000000:
 48 8d 25 51 3f 60 01
leaq asdf asdf asdf

使用您的 awk 代码并进行一些更改:

echo -e "ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf" | awk -v OFS='\n' '
{
sub(/: */,":\t")
split([=10=],field,/[\t]/)
print "length of field:" length(field), field[1], field[2],field[3]
}'
length of field:3
ffffffff81000000:
48 8d 25 51 3f 60 01
leaq asdf asdf asdf

如您所见:

  • 添加了带有 sub()
  • 的选项卡
  • 所以 split() 的分隔符只有 [\t],
  • OFS\n
  • 最后只有一个print.

您可以使用 sub: 替换为 :\t,将 \t 替换为 \n。你不会在一行 awk 文本中找到 \n 除非你的编程操作把它放在那里;因此它是一个有用的分隔符。您现在可以拆分 \n,您的代码将按您想象的那样工作:

echo -e "ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf" \
| awk '{sub(/: /,":\t"); gsub(/\t/,"\n"); split([=10=],field,/\n/)
print "length of field:" length(field)
for (x=1; x<=length(field); x++) print field[x]}'

打印:

length of field:3
ffffffff81000000:
48 8d 25 51 3f 60 01
leaq asdf asdf asdf

恕我直言,对于这样的工作,您应该使用 GNU awk 作为第三个参数来 match() 而不是 split():

$ echo -e "ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf" |
awk '
    match([=10=],/([^:]+:)\s*([^\t]+)\t(.*)/,field) {
        print "length of field:" length(field);for (x in field) print x, field[x]
    }
'
length of field:12
0start 1
0length 58
3start 40
1start 1
2start 19
3length 19
2length 20
1length 17
0 ffffffff81000000: 48 8d 25 51 3f 60 01        leaq asdf asdf asdf
1 ffffffff81000000:
2 48 8d 25 51 3f 60 01
3 leaq asdf asdf asdf

请注意,结果数组包含的信息比仅填充与正则表达式段匹配的字符串的 3 个字段更多的信息。如果您不需要额外的字段,请忽略它们:

$ echo -e "ffffffff81000000: 48 8d 25 51 3f 60 01\tleaq asdf asdf asdf" |
awk '
    match([=11=],/([^:]+:)\s*([^\t]+)\t(.*)/,field) {
        for (x=1; x<=3; x++) print x, field[x]
    }
'
1 ffffffff81000000:
2 48 8d 25 51 3f 60 01
3 leaq asdf asdf asdf