【发布时间】:2022-01-07 23:24:26
【问题描述】:
bash 有没有办法从一个匹配项打印到另一个匹配项,除非第三个匹配项在这些行之间?假设文件是:
A
B
C
D
E
A
B
Z
C
D
我想打印“A”和“C”之间的所有行,而不是那些包含“Z”的行,所以输出应该是:
A
B
C
我正在使用这部分代码来匹配“A”和“C”之间的行:
awk '/C/{p=0} /A/{p=1} p'
【问题讨论】:
标签: awk
bash 有没有办法从一个匹配项打印到另一个匹配项,除非第三个匹配项在这些行之间?假设文件是:
A
B
C
D
E
A
B
Z
C
D
我想打印“A”和“C”之间的所有行,而不是那些包含“Z”的行,所以输出应该是:
A
B
C
我正在使用这部分代码来匹配“A”和“C”之间的行:
awk '/C/{p=0} /A/{p=1} p'
【问题讨论】:
标签: awk
使用您展示的示例,请尝试关注awk 代码。
awk '
/A/ { found=1 }
/C/ && !noVal && found{
print value ORS $0
found=noVal=value=""
}
found && /Z/{ noVal=1 }
found{
value=(value?value ORS:"")$0
}
' Input_file
说明:为上述添加详细说明。
awk ' ##Starting awk program from here.
/A/ { found=1 } ##Checking condition if line has A then set found to 1.
/C/ && !noVal && found{ ##Checking if C is found and noVal is NULL and found is set then do following.
print value ORS $0 ##printing value ORS and current line here.
found=noVal=value="" ##Nullifying found,noVal and value here.
}
found && /Z/{ noVal=1 } ##Checking if found is SET and Z is found then set noVal here.
found{ ##Checking if found is set here.
value=(value?value ORS:"")$0 ##Creating value which has current line in it and keep concatenating values to it.
}
' Input_file ##Mentioning Input_file name here.
【讨论】:
我会使用A 作为记录分隔符,使用C 作为字段分隔符。因此,A 到 C 的范围将在 $1 中(除了开头的 A 本身和结尾的 C),其余的直到 $2 中的下一个 A .
诀窍是只打印第一个字段$1,如果它不包含任何Z。跳过第一条为空的记录。
那就试试吧:
awk 'BEGIN{RS="A";FS="C"}(NR > 1) && !/Z/{print "A" $1 "C"}' inputfile
根据下面 Ed Morton 的评论,甚至更好:
awk 'BEGIN{RS="A";FS="C"}(NR > 1) && !/Z/{print RS $1 FS}' inputfile
如果Z 可以出现在C 之后,我们将不得不更正代码。
【讨论】:
这使用全行字符串匹配而不是您的问题中使用的部分行正则表达式匹配以及到目前为止发布的其他答案(请参阅how-do-i-find-the-text-that-matches-a-pattern 了解差异),因为我希望这是您真正应该使用的强大的解决方案:
$ cat tst.awk
$0 == "A" { inBlock=1 }
inBlock {
rec = rec $0 ORS
if ( $0 == "C" ) {
if ( !index(ORS rec ORS, ORS "Z" ORS) ) {
printf "%s", rec
}
rec = ""
inBlock = 0
}
}
$ awk -f tst.awk file
A
B
C
如果你真的想继续使用部分行正则表达式匹配,那就是:
$ cat tst.awk
/A/ { inBlock=1 }
inBlock {
rec = rec $0 ORS
if ( /C/ ) {
if ( rec !~ /Z/ ) {
printf "%s", rec
}
rec = ""
inBlock = 0
}
}
但如果您的真实数据不只是单独一行中的单个字母,那将是脆弱的。
【讨论】:
您可以使用 sed 执行您所描述的操作:
sed '/^A$/,/^C$/!d; /^Z$/d' example-data
# gives
A
B
C
A
B
C
!d 表示删除与地址不匹配的行。
您的预期结果是三行。所以你可以使用sed '/^A$/,/^C$/!d; /^Z$/d; /^C$/q'。或者,sed '/^A$/,/^C$/!d; /^Z$/d' | sort -u?
【讨论】: