对于由分隔符“:”分隔的字符串,匹配分隔符之间包含不同字符串“XXX”的所有子字符串的最佳方式是什么。
例如,首先
/aa/:/a/b/XXX/:/bb/bb:/c/XXXd/e/f/:/cc/cc/
删除所有包含“XXX”的部件——可以是任意数量的实例,在任何位置——最终得到
/aa/:/bb/bb:/cc/cc/
bash 可以直接执行此操作吗?用 awk 还是 sed 更好?
您要消除的每个条目都是一系列非
:
,其中包含XXX
,在正则表达式世界中是[^:]*XXX[^:]*
但是您还想消除它后面的
:
,这意味着您想要匹配并消除 [^:]*XXX[^:]*:
。
实际上,如果恰好是最后一个包含
XXX
的字段,则不允许您将其删除;要解决此问题,您需要匹配行尾作为结束 :
的替代方案,因此命令是
sed -E 's/[^:]*XXX[^:]*(:|$)//g' the_file
但是这仍然有一个问题:一旦最后一项匹配,它就会留下尾随
:
。为了解决这个问题,我们可以简单地运行另一个替换ad hoc,这样完整的 Sed 命令就是这样的,
sed -E 's/[^:]*XXX[^:]*(:|$)//g;s/:$//' the_file
我们真的需要
s
usbstitution 命令吗?
Sed 没有前瞻,这意味着我们匹配的任何内容都会被消耗,并且无法通过相同的 s
命令再次匹配,即使有
g
标志也是如此。另一方面,我们希望将包含
XXX
的字段连同其周围的两个
:
中的一个(但不是全部)一起消除。如果我们像我一样选择正确的
:
,那么很明显,如果最后一个字段(与行尾匹配,而不是
:
)匹配XXX
,则结果将带有尾随:
(除非所有字段都匹配 XXX
,在这种情况下结果为空字符串)。这意味着单个 s
ubstitution 命令无法为您的用例的所有场景提供“干净”的答案。
使用两个s
命令足以解决此问题,正如我所展示的。
$ awk -v RS=':' '{ORS=RT} !/XXX/' file
/aa/:/bb/bb:/cc/cc/
为了适应恩里科在下面的评论中提到的情况
$ cat file
/aa/:/a/b/XXX/:/bb/bb:/c/XXXd/e/f/:/cc/cc/
$ cat file1
/bb/bb:/aXXX/b/
您可以使用 GNU awk for multi-char RS 来完成此操作:
$ awk -v RS='[:\n]' '!/XXX/{printf "%s%s", sep, $0; sep=":"} END{print ""}' file
/aa/:/bb/bb:/cc/cc/
$ awk -v RS='[:\n]' '!/XXX/{printf "%s%s", sep, $0; sep=":"} END{print ""}' file1
/bb/bb
或使用任何 awk:
$ awk -v RS=':' '!/XXX/{sub(/\n/,""); printf "%s%s", sep, $0; sep=":"} END{print ""}' file
/aa/:/bb/bb:/cc/cc/
$ awk -v RS=':' '!/XXX/{sub(/\n/,""); printf "%s%s", sep, $0; sep=":"} END{print ""}' file1
/bb/bb
awk ' BEGIN {FS=OFS=":"}
{s="";
for (i=1;i<=NF;i++) {
if ($i~/XXX/) continue;
s=s OFS $i
}
print s
}' file