Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
String 高效/安全的shell脚本,用于从分隔的拆分字符串中删除所有子字符串匹配项?_String_Bash_Shell_Awk_Sed - Fatal编程技术网

String 高效/安全的shell脚本,用于从分隔的拆分字符串中删除所有子字符串匹配项?

String 高效/安全的shell脚本,用于从分隔的拆分字符串中删除所有子字符串匹配项?,string,bash,shell,awk,sed,String,Bash,Shell,Awk,Sed,对于由分隔符“:”分割的字符串,在分隔符之间匹配包含不同字符串“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”的所有子字符串的最佳方法是什么

比如说,首先

/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的//[^:]*XXX[^:]*(:|$)//g'该文件
但是这仍然有一个问题:只要最后一项匹配,它就会留下一个尾随的
。为了解决这个问题,我们只需运行另一个临时替换,因此完整的Sed命令如下:

sed-E的//[^:]*XXX[^:]*(:|$)//g;s/:$/“该文件
我们真的需要
s
usbstitution命令吗

Sed没有lookaheads,这意味着我们匹配的任何内容都会被消耗,并且不能通过相同的
s
命令再次匹配,即使有
g
标志

另一方面,我们希望将包含
XXX
的字段与其周围的两个
中的一个(而不是两个)一起删除

如果我们像我一样选择了正确的
,那么很明显,如果最后一个字段(与行的尾端匹配,而不是
)匹配
XXX
,它将留下一个尾随
(除非所有字段都匹配
XXX
,在这种情况下,结果是空字符串)

这意味着一个
s
substitution命令无法为用例的所有场景提供“干净”的答案


如我所示,使用两个
s
命令就足以解决此问题。

下面是一个解决此问题的awk:

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

对于(i=1;i,对于多字符RS和RT,GNU awk:

$ 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为多字符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

也许
XXX
字符串可能会出现在一行的开头,因此可能
sed-E的/:?[^:]*XXX[^:]*//g'文件
?@potong,如果
XXX
出现在第一个字段,而不是所有其他字段,例如
sed-E的/:?[^:]*XXX[^:]*//g'这仍然在像
/aXXX/b/:/bb/bb
这样的输入上保留了一个前导的
。这可以通过将
s=s of s$i
更改为
if(s==“”){s=$i}或者{s=s of s$i}来解决
。如果输入是
/bb/bb:/aXXX/b/
,这仍然会忘记删除
,但是+1。@EnricoMariaDeAngelis更糟糕的是,它没有生成终止换行符,因此输出不是有效的POSIX文本文件。感谢提醒,我现在已经修复了它。哦,对了,因为POSIX行与
^[^\n]*\n
,我没有想到。这意味着,即使我想,我也不能通过管道将输出传输到另一个工具(如sed)来删除终止的
,因为根据POSIX,这是未定义的行为。