高效/安全的shell脚本从分隔的分割字符串中删除所有子字符串匹配?

问题描述 投票:0回答:3

对于由分隔符“:”分隔的字符串,匹配分隔符之间包含不同字符串“XXX”的所有子字符串的最佳方式是什么。

例如,首先

/aa/:/a/b/XXX/:/bb/bb:/c/XXXd/e/f/:/cc/cc/

删除所有包含“XXX”的部件——可以是任意数量的实例,在任何位置——最终得到

/aa/:/bb/bb:/cc/cc/

bash 可以直接执行此操作吗?用 awk 还是 sed 更好?

string bash shell awk sed
3个回答
4
投票

您要消除的每个条目都是一系列非

:
,其中包含
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

命令足以解决此问题,正如我所展示的。

    


3
投票

$ 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



2
投票

awk ' BEGIN {FS=OFS=":"} {s=""; for (i=1;i<=NF;i++) { if ($i~/XXX/) continue; s=s OFS $i } print s }' file

© www.soinside.com 2019 - 2024. All rights reserved.