使用多个定界符的 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-awk
的RS
(用于记录分隔符)变量:
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
RTprints
RT`变量
如果您还想打印字段的长度,请使用:
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
我正在尝试使用 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-awk
的RS
(用于记录分隔符)变量:
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
RTprints
RT`变量
如果您还想打印字段的长度,请使用:
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