Sed:修改文本中每个单词的每个非第一个单词重复

Sed:修改文本中每个单词的每个非第一个单词重复

我需要使用 sed 做类似的事情吗?

qq    ab xyz     ab qq aa ab 

变成:

qq    ab xyz     +ab+ +qq+ aa +ab+

答案1

如果您的输入不包含<,>也不+包含字符,您可以这样做:

sed '
  s/[[:alnum:]]\{1,\}/<&>/g;:1
  s/\(<\([^>]*\)>.*\)<\2>/\1+\2+/;t1
  s/[<>]//g'

如果可以的话,你总是可以逃避它们:

sed '
  s/:/::/g;s/</:{/g;s/>/:}/g
  s/[[:alnum:]]\{1,\}/<&>/g;:1
  s/\(<\([^>]*\)>.*\)<\2>/\1+\2+/;t1
  s/[<>]//g
  s/:}/>/g;s/:{/</g;s/::/:/g'

这些假设您想在每一行上独立地执行此操作。如果您想对整个文件执行此操作,则需要首先将整个文件加载到内存中(请注意,某些sed实现有大小限制):

sed '
  :2
  $!{N;b2
  }
  s/:/::/g;s/</:{/g;s/>/:}/g
  s/[[:alnum:]]\{1,\}/<&>/g;:1
  s/\(<\([^>]*\)>.*\)<\2>/\1+\2+/;t1
  s/[<>]//g
  s/:}/>/g;s/:{/</g;s/::/:/g'

但这会非常低效,并且使用以下命令会容易得多perl

perl -pe 's/\w+/$seen{$&}++ ? "+$&+" : $&/ge'

基于线路:

perl -pe 'my %seen;s/\w+/$seen{$&}++ ? "+$&+" : $&/ge'

答案2

这是另一种方法:这使用了一些seds:

an='[:alnum:]' esc=$(printf '\033\[')
sed "/[${an}]/!d;=;a\ }
    s/.*/ & /;s/[^${an}]\{1,\}/   /g
    s| \([${an}"']\{1,\}\) | \
    s/\\([^+'"${an}"']\\)\\(\1\\)\\([^+'"${an}"']\\)/\\1+\\2+\\3/2|g
' <text |
sed '/^ /!N;s/\n */{/' |
sed -e 's/.*/ & /;s/+/ & /g' \
    -f - \
    -e "s/ //;s/ $//
        s/+[^+ ]\{1,\}+/${esc}38;5;35m&${esc}0m/g
        s/ + /+/g" text

基本上,前两个sed团队合作为第三个编写剧本。第一个sed清除每行中除字母数字字符之外的所有内容 - 并完全跳过任何没有行的行。对于剩下的所有字符组,它会编写一个替换语句,第三个字符最终将(几乎是瞬间)阅读并解释其脚本。

第二个sed是必要的,因为第一个按行编写脚本 - 并且每行可以有多个s///语句。第一个打印包含字母数字的每一行的行号,但需要在第三个的函数上下文中配对sed- 所以第二个这样做。

以下是脚本的示例:

...
43{
    s/\([^+[:alnum:]]\)\(n\)\([^+[:alnum:]]\)/\1+\2+\3/2  
    s/\([^+[:alnum:]]\)\(N\)\([^+[:alnum:]]\)/\1+\2+\3/2  
    s/\([^+[:alnum:]]\)\(G\)\([^+[:alnum:]]\)/\1+\2+\3/2  
 }
44{
    s/\([^+[:alnum:]]\)\(b\)\([^+[:alnum:]]\)/\1+\2+\3/2  
    s/\([^+[:alnum:]]\)\(block\)\([^+[:alnum:]]\)/\1+\2+\3/2  
 }
45{
    s/\([^+[:alnum:]]\)\(END\)\([^+[:alnum:]]\)/\1+\2+\3/2  
    s/\([^+[:alnum:]]\)\(SEDSCRIPT\)\([^+[:alnum:]]\)/\1+\2+\3/2  
 }

2每个都有一个尾随s///- 这是因为每个替换都是针对每个模式的第二次出现 - 如果第二次出现不存在,则不会进行任何替换。上面是在我的另一个sed脚本上运行它的结果 - 它似乎不受特殊字符或类似字符的影响。

当我写作时,我发现如果我给它的选择着色,就更容易知道发生了什么 - 这就是......

s/+[^+ ]\{1,\}+/${esc}38;5;35m&${esc}0m/g

...那条线确实如此。您可以评论或删除它(您曾经使用过它并且不想要或不需要它)。

这是它为示例数据编写的脚本:

1{
    s/\([^+[:alnum:]]\)\(qq\)\([^+[:alnum:]]\)/\1+\2+\3/2 
    s/\([^+[:alnum:]]\)\(ab\)\([^+[:alnum:]]\)/\1+\2+\3/2 
    s/\([^+[:alnum:]]\)\(xyz\)\([^+[:alnum:]]\)/\1+\2+\3/2 
    s/\([^+[:alnum:]]\)\(ab\)\([^+[:alnum:]]\)/\1+\2+\3/2 
    s/\([^+[:alnum:]]\)\(qq\)\([^+[:alnum:]]\)/\1+\2+\3/2 
    s/\([^+[:alnum:]]\)\(aa\)\([^+[:alnum:]]\)/\1+\2+\3/2 
    s/\([^+[:alnum:]]\)\(ab\)\([^+[:alnum:]]\)/\1+\2+\3/2
 }

以下是其打印内容:

qq    ab xyz     +ab+ +qq+ aa +ab+

sed以及我之前的一些脚本:

        s/\(\(.\)${bs}\2\)\{1,\}/${esc}38;5;35m&${+esc+}0m/g
        s/\(_${bs}[^_]\)\{1,\}/${esc}38;5;75m&${+esc+}0m/g
        s/.${bs}//g
        s/\(\(${esc}\)0m\2[^m]*+m+[_ ]\{,+2+\}\)\{+2+\}/_/g
n;      /./!N;G

相关内容