繁体   English   中英

使用sed / awk从子节中删除字符串

[英]Using sed/awk to remove string from subsections

我有一个看起来像这样的文件:

bar
barfo
barfoo
barfooo
barfoooo

sample
sampleText1
sampleText2
sampleText3

prefix
prefixFooBar
prefixBarFoo

我想要sed(或awk)做的是从其所有内容中删除引入节的字符串,以便我最终得到:

bar
fo
foo
fooo
foooo

sample
Text1
Text2
Text3

prefix
FooBar
BarFoo

我试过用

sed -e -i '/([[:alpha:]]+)/,/^$/ s/\1//g' file

但是,“无效的反向引用”失败了。

$ awk '{$0=substr($0,idx)} !idx{idx=length($0)+1} !NF{idx=0} 1' file
bar
fo
foo
fooo
foooo

sample
Text1
Text2
Text3

prefix
FooBar
BarFoo

另一个awk

$ awk '{sub(pre,"")}1; !NF{pre=""} !pre{pre=$1}' file

bar
fo
foo
fooo
foooo

sample
Text1
Text2
Text3

prefix
FooBar
BarFoo
perl -ple'
   if (!length($_)) { $re = "" }
   elsif (!length($re)) { $re = $_ }
   else { s/^\Q$re// }
'

笔记:

  • 使用s/\\Q$re//g删除行中的任何位置,而不是仅删除前缀。
  • 即使标题行包含特殊字符,例如\\ , . *
  • 即使一行中有多个空行,这也可以工作。
  • 有关完整用法,请参阅指定要处理到Perl one-liner的文件
  • 代码中的换行符是可选的(即可以删除)。

一个sed解决方案,主要是为了说明sed可能不是这样做的最佳选择:

$sed -E '1{h;b};/^$/{n;h;b};G;s/^(.*)(.*)\n\1$/\2/' infile
bar
fo
foo
fooo
foooo

sample
Text1
Text2
Text3

prefix
FooBar
BarFoo

下面是它的工作原理:

1 {                   # on the first line
  h                   # copy pattern buffer to hold buffer
  b                   # skip to end of cycle
}
/^$/ {                # if line is empty
  n                   # get next line into pattern buffer
  h                   # copy pattern buffer to hold buffer
  b                   # skip to end of cycle
}
G                     # append hold buffer to pattern buffer
s/^(.*)(.*)\n\1$/\2/  # substitute

复杂的部分在于替代。 在替换之前,模式缓冲区包含如下内容:

prefixFooBar\nprefix

替换现在匹配两个捕获组,第一个是由\\n和字符串结尾之间的内容引用的 - 我们从保持缓冲区中获取的前缀。

然后替换为原始行的其余部分,并删除前缀。

备注:

  • 这适用于GNU sed; 较旧的GNU sed版本可能需要-r而不是-E
  • -E只是为了方便; 没有它,替换看起来像

     s/^\\(.*\\)\\(.*\\)\\n\\1$/\\2/ 

    但仍然有效。

  • 对于macOS sed,它适用于命令之间的文字换行:

     sed -E '1{ h b } /^$/{ n h b } G s/^(.*)(.*)\\n\\2$/\\2/' infile 

这是另一个sed解决方案。 仅当段落中的所有字符串都以主题行开头时 ,它才有效。

sed -e '1{h;b};/^$/{n;h;b};H;g;s/\(.*\)\n\1//;p;g;s/\n.*//;h;d' file
  • 1第一行: h复制以保留空格, b打印并继续下一行
  • /^$/空行: n打印并读取下一行, h复制以保存空格, b打印并继续
  • 所有(其他)行:
    • H附加换行符
    • g复制持有空间以模式空间
    • s/\\(.*\\)\\n\\1//从模式空间中删除第一行及其第二行中的内容
    • p打印图案空间
    • g复制保留空间以模式空间以从H删除新内容
    • /\\n.*//删除新内容
    • h复制回来占据空间
    • d删除模式空间

sed对这些东西没用。

你得到'无效的后向引用',因为s的搜索模式中没有组。

awk中的另一个:

$ awk '{if(p&&match($0,"^" p))$0=substr($0,RLENGTH+1);else p=$0}1' file

输出:

bar
fo
foo
fooo
foooo

sample
Text1
Text2
Text3

prefix
FooBar
BarFoo

这是另一个awk解决方案:

awk '{gsub(s,"")}1; s==""||!NF{s=$0}' file

优点:

  • 匹配被替换,无论他们在哪里
  • 所有比赛都被替换
  • 头线可以评估为0 / false
  • 头线可能包含空格

缺点:

  • 头行不得包含正则表达式元字符

这可能适合你(GNU sed):

sed 'G;s/^\(.\+\)\(.*\)\n\1$/\2/;t;s/\n.*//;h' file

将前一个键(如果它是第一行,则没有任何内容)附加到当前行。 如果它们匹配,则移除键和上一个键,打印当前行并重复。 否则密钥不匹配,删除旧的附加密钥,将新密钥存储在保留空间中并打印新密钥。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM