AWK 搜索特定序列,如果找到则在下一行搜索另一个序列
AWK to search a specific sequence and if found search in the next line another sequence
我试图找到一个 txt 格式的字符串,每次找到它时,都会查找一个特定的字符串以更改为另一个字符串,并避免读取该行的第一个序列。
想象下一个十六进制的 txt:
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 17 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
预期输出:
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
我每次遇到 4b 序列时都需要查找 14 序列,如果找到,则在下一行查找第一个字符串,在本例中为 17,如果该字符串为 17,则更改为 12。左边有一个序列,它给你你是txt的行,所以分析起来没意思,因为它在每个段落中重复
我有的是下一个:
gawk ' { for ( i = 1; i <= NF; ++i ) {
if ( $i == "4b" )
r = 1
if ( r && ($i == "14" ))
t = 1
if ( r && t && $i == "17") {
r = 0
t = 0
$i = "12"
}
}
}
1 ' example.txt example2.txt
但是,我不太清楚如何避免读取每行的第一个 xxxx 序列
根据您展示的示例,您能否尝试使用 GNU awk
进行以下编写和测试。
awk '
!NF{ found1=found2=0 }
/(^|[[:space:]])4b([[:space:]]|$)/{
found1=1
print
next
}
found1 && /(^|[[:space:]])14([[:space:]]|$)/{
found2=1
print
next
}
found1 && found2{
for(i=2;i<=NF;i++){
if($i==17){ $i=12 }
}
print
next
}
1
' Input_file
说明: 为以上添加详细说明。
awk ' ##Starting awk program from here.
!NF{ found1=found2=0 }
/(^|[[:space:]])4b([[:space:]]|$)/{ ##Checking condition if line has 4b with spaces or coming in starting or ending of line.
found1=1 ##Then set found to 1 here.
print ##Printing the current line here.
next ##next will skip all further statements from here.
}
found1 && /(^|[[:space:]])14([[:space:]]|$)/{ ##Checking if found1 is SET AND if line has 14 with spaces or coming in starting or ending of line.
found2=1 ##Setting found2 to 1 here.
print ##Printing the current line here.
next ##next will skip all further statements from here.
}
found1 && found2{ ##Checking condition if found1 and found2 is SET then do following.
for(i=2;i<=NF;i++){ ##Traversing through all fields here starting from 2nd field.
if($i==17){ $i=12 } ##Checking condition if field value is 17 then make it 12.
}
print ##Printing current line.
next ##next will skip all further statements from here.
}
1 ##1 will print current line.
' Input_file ##Mentioning Input_file name here.
就像在生活中一样,在处理数据时,根据过去发生的事情(您已阅读的数据)而不是将来会发生的事情(您将要阅读的数据)来做出决定要容易得多,所以相反说“如果我有 X,它后面的东西是 Y”,把你的要求写成“如果我有 Y,它前面的东西是 X”,实现它的软件通常会变得更加简单明了。
这是你想要做的吗(在每个 Unix 机器上使用任何 shell 中的任何 awk):
$ cat tst.awk
( == 17) && (p1 ~ / 14 /) && (p2 ~ / 4b /) {
sub(/ 17 /," 12 ")
}
{ p2=p1; p1=[=10=]" "; print }
$ awk -f tst.awk file
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
如果这不是您所需要的全部,请编辑您的问题以阐明您的要求并提供更真实全面的示例input/output,包括上述方法不适用的情况。
我在上面使用 sub(/ 17 /," 12 ")
而不是 =12
来保留字段之间的白色 space。这样做是安全的,因为目标字段是 $2,如果它是任何其他字段,您不能将其作为目标字段之前的字段也可能是 17。有各种 sub()/match()/substr()当然有办法处理。
你尝试的awk
命令很不错,你只需要确保使用-v RS=
(空RS
)使每个段落成为一条记录。
以下内容在 gnu-awk
中应该对您有用:
cat fmt.awk
{
ORS = RT # set ORS same RT variable populated using RS
}
{
r = t = p = ""
for ( i = 1; i <= NF; ++i ) {
# set r = 1 when we get 4b
if ( $i == "4b" )
r = 1
# set t = 1 when we get 14 when r==1
if ( r && $i == "14" )
t = 1
# when we get 4 digits save the position
if ($i ~ /^[0-9]{4}$/)
p = i+1
# replace 17 with 12 when we get 17 when t==1
if ( t && p == i && $i == "17" ) {
[=10=] = gensub("((\S+\s+){"i-1"})\S+", "\112", 1)
break
}
}
} 1
运行 为:
awk -v RS= -f fmrt.awk file
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
我试图找到一个 txt 格式的字符串,每次找到它时,都会查找一个特定的字符串以更改为另一个字符串,并避免读取该行的第一个序列。
想象下一个十六进制的 txt:
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 17 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
预期输出:
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
我每次遇到 4b 序列时都需要查找 14 序列,如果找到,则在下一行查找第一个字符串,在本例中为 17,如果该字符串为 17,则更改为 12。左边有一个序列,它给你你是txt的行,所以分析起来没意思,因为它在每个段落中重复
我有的是下一个:
gawk ' { for ( i = 1; i <= NF; ++i ) {
if ( $i == "4b" )
r = 1
if ( r && ($i == "14" ))
t = 1
if ( r && t && $i == "17") {
r = 0
t = 0
$i = "12"
}
}
}
1 ' example.txt example2.txt
但是,我不太清楚如何避免读取每行的第一个 xxxx 序列
根据您展示的示例,您能否尝试使用 GNU awk
进行以下编写和测试。
awk '
!NF{ found1=found2=0 }
/(^|[[:space:]])4b([[:space:]]|$)/{
found1=1
print
next
}
found1 && /(^|[[:space:]])14([[:space:]]|$)/{
found2=1
print
next
}
found1 && found2{
for(i=2;i<=NF;i++){
if($i==17){ $i=12 }
}
print
next
}
1
' Input_file
说明: 为以上添加详细说明。
awk ' ##Starting awk program from here.
!NF{ found1=found2=0 }
/(^|[[:space:]])4b([[:space:]]|$)/{ ##Checking condition if line has 4b with spaces or coming in starting or ending of line.
found1=1 ##Then set found to 1 here.
print ##Printing the current line here.
next ##next will skip all further statements from here.
}
found1 && /(^|[[:space:]])14([[:space:]]|$)/{ ##Checking if found1 is SET AND if line has 14 with spaces or coming in starting or ending of line.
found2=1 ##Setting found2 to 1 here.
print ##Printing the current line here.
next ##next will skip all further statements from here.
}
found1 && found2{ ##Checking condition if found1 and found2 is SET then do following.
for(i=2;i<=NF;i++){ ##Traversing through all fields here starting from 2nd field.
if($i==17){ $i=12 } ##Checking condition if field value is 17 then make it 12.
}
print ##Printing current line.
next ##next will skip all further statements from here.
}
1 ##1 will print current line.
' Input_file ##Mentioning Input_file name here.
就像在生活中一样,在处理数据时,根据过去发生的事情(您已阅读的数据)而不是将来会发生的事情(您将要阅读的数据)来做出决定要容易得多,所以相反说“如果我有 X,它后面的东西是 Y”,把你的要求写成“如果我有 Y,它前面的东西是 X”,实现它的软件通常会变得更加简单明了。
这是你想要做的吗(在每个 Unix 机器上使用任何 shell 中的任何 awk):
$ cat tst.awk
( == 17) && (p1 ~ / 14 /) && (p2 ~ / 4b /) {
sub(/ 17 /," 12 ")
}
{ p2=p1; p1=[=10=]" "; print }
$ awk -f tst.awk file
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19
如果这不是您所需要的全部,请编辑您的问题以阐明您的要求并提供更真实全面的示例input/output,包括上述方法不适用的情况。
我在上面使用 sub(/ 17 /," 12 ")
而不是 =12
来保留字段之间的白色 space。这样做是安全的,因为目标字段是 $2,如果它是任何其他字段,您不能将其作为目标字段之前的字段也可能是 17。有各种 sub()/match()/substr()当然有办法处理。
你尝试的awk
命令很不错,你只需要确保使用-v RS=
(空RS
)使每个段落成为一条记录。
以下内容在 gnu-awk
中应该对您有用:
cat fmt.awk
{
ORS = RT # set ORS same RT variable populated using RS
}
{
r = t = p = ""
for ( i = 1; i <= NF; ++i ) {
# set r = 1 when we get 4b
if ( $i == "4b" )
r = 1
# set t = 1 when we get 14 when r==1
if ( r && $i == "14" )
t = 1
# when we get 4 digits save the position
if ($i ~ /^[0-9]{4}$/)
p = i+1
# replace 17 with 12 when we get 17 when t==1
if ( t && p == i && $i == "17" ) {
[=10=] = gensub("((\S+\s+){"i-1"})\S+", "\112", 1)
break
}
}
} 1
运行 为:
awk -v RS= -f fmrt.awk file
0000 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
0010 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
0030 07 91 94 71 06 00 07 19
0000 09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01
0010 00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14
0020 12 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
0030 00 07 19