SED替换模式的少数首次出现(和范围)

SED替换模式的少数首次出现(和范围),sed,replace,range,find-occurrences,Sed,Replace,Range,Find Occurrences,是否可以使用SED(与SED-r的/[^[:space:][]*/TEST/4g'相反)更改此场景中字符串的前4次(或更多)出现: 我使用AWK两次颠倒单词顺序,但这很长,很复杂,我只想用SED实现: echo one two three four five six seven | awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}' | sed -r 's/[^ ]*/TEST/4g' | a

是否可以使用SED(与
SED-r的/[^[:space:][]*/TEST/4g'
相反)更改此场景中字符串的前4次(或更多)出现:

我使用AWK两次颠倒单词顺序,但这很长,很复杂,我只想用SED实现:

echo one two three four five six seven | awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}'  | sed -r 's/[^ ]*/TEST/4g' |  awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}'
也可能有改变发生范围的选项,如3-5,6-12

示例输入为:

一二三四五六七

891011121314<P> 15161718191921


一个AWK怎么样

awk '{for(i=1;i<=NF;i++) if(i<5){$i="TEST"}; print}'
对新提供的输入文件进行测试运行:

$ awk '{for(i=3;i<6;i++)$i="TEST";print}' input
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one
$ perl -pe '$c=0;s/\S+/++$c~~[3..5]?"TEST":$&/ge' input
Smartmatch is experimental at -e line 1. <== This is a warning that goes to STDERR
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one
$perl-pe'$c=0;s/\s++/++$c~~[3..5]?“测试”:$&/ge输入

Smartmatch在-e 1号线进行试验 答案已由我方提供注意:如果要处理一个范围,需要使用最大界限,因为它将在不引发任何异常/错误的情况下处理尽可能多的匹配

GNU sed:

echo 'one two three four five six seven' | \
  sed 's/[^[:space:]]*/\n&/g;:t;/\n/{x;/.\{4\}/!{s/$/./;x;s/\n[^[:space:]]*/TEST/;bt};x};s/\n//g'
POSIX sed:

nl='
';
echo 'one two three four five six seven' | sed "s/[^[:space:]]*/\\$nl&/g;:t${nl}/\n/{x;/.\{4\}/!{${nl}s/$/./;x;s/\n[^[:space:]]*/TEST/;bt$nl};x$nl};s/\n//g"

原始解释(注意此处,
1
替换为
2
,您可以使用任何其他模式):

在这里,我使用了两种值得注意的技巧。首先,每年 行中出现的
1
替换为
\n1
。就这样,我 接下来进行递归替换,我可以确定不会替换 如果替换字符串包含替换,则出现两次 一串例如,如果我用
hey
替换
he
,它仍然可以工作

我这样做:

s/1/\
&/g
第二,我在计算替换数时,在
h
每次出现的旧空格。一旦我到了三岁,就再也不会发生了。如果 将其应用于数据,并将
\{3\}
更改为总计 您所需的替换,以及
/\n1/
对您所指内容的地址 要替换,您应该只替换您希望替换的数量


对于sed来说,这是一个完全不合适的任务,因为sed用于对单个字符串执行简单的
s/old/new/
仅此而已。在每个UNIX设备上的任意shell中使用任意awk:

$ echo one two three four five six seven | awk '{for (i=1; i<=4; i++) $i="TEST"}1'
TEST TEST TEST TEST five six seven

$ echo one two three four five six seven | awk '{for (i=3; i<=5; i++) $i="TEST"}1'
one two TEST TEST TEST six seven
$echo一二三四五六七| awk'{for(i=1;i
说明:

  • :r
    要分支回的名为r的标签
  • s/(^ |(TEST)+)[^]*/\1TEST/;
    替换,仅替换一个非测试词的出现,在该行开头或一个或多个测试之前
  • /^(TEST){4}/!br'
    regex查找所需内容,然后是
    !br
    ,如果尚未匹配,则分支回
    :r

显然,这是脆弱的。如果任何一行没有四个单词,它将无限循环。可能只会被GNU使用。

awk更好,在编写它六个月后,您将无法理解一个神秘的sed命令。这个答案不会像这里那样起作用,搜索的文本不是静态的。不过,那里还有其他答案可能适合这里。@corentilimier我知道这个选项,它只适用于同一个单词:)更改一行中前四个字符串的一种简单方法是为要替换的字符串添加标记,例如,
sed's/\s\+/\n&/g;s/\n//5g;s/\n\s\+/TEST/g'文件
行中的范围可以使用类似的方法实现。这是可以的,但我正在搜索基于sed的内容,如果这是可能的,并且非常容易实现和使用emember.@mike是的,你明确表示你正在寻找一个简单的sed解决方案。我想知道这是否只是为了学习sed(在这种情况下,“不可能”可能是一个答案),或者手头的问题强加了一些要求(在这种情况下,提供多一点上下文可以得到更好的答案)@mike with sed除了
s/old/new/
之外的任何东西都不会
很容易实现和记住。
相反,它将是一个噩梦般的符文集合,当你在6个月后的代码中遇到它时,你会在睡梦中呜咽,需要理解它。是的,我曾想过这一点,但后来我重新阅读了它e OP寻找不长、不复杂的东西,放弃了这个想法。不得不在simple和sed这两个同样无法解释的要求之间进行选择,我选择了第一个,放弃了第二个。然而,这是学习sed的一个很好的练习,这就是目标。哇,这确实有效,但非常复杂,我认为有使此大小写易于实现和理解的方法。在括号内,字符“|”在“^”之后的作用是,竖条是一个“or”。
(alice | bob)
匹配任意一个单词。
^
可能看起来像两个逻辑运算符,但它是
^
匹配模式空间的开头,后跟or。
perl -pe 's/\S+/++$c~~[3..5]?"TEST":$&/ge'
$ perl -pe '$c=0;s/\S+/++$c~~[3..5]?"TEST":$&/ge' input
Smartmatch is experimental at -e line 1. <== This is a warning that goes to STDERR
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one
echo 'one two three four five six seven' | \
  sed 's/[^[:space:]]*/\n&/g;:t;/\n/{x;/.\{4\}/!{s/$/./;x;s/\n[^[:space:]]*/TEST/;bt};x};s/\n//g'
nl='
';
echo 'one two three four five six seven' | sed "s/[^[:space:]]*/\\$nl&/g;:t${nl}/\n/{x;/.\{4\}/!{${nl}s/$/./;x;s/\n[^[:space:]]*/TEST/;bt$nl};x$nl};s/\n//g"
s/1/\
&/g
$ echo one two three four five six seven | awk '{for (i=1; i<=4; i++) $i="TEST"}1'
TEST TEST TEST TEST five six seven

$ echo one two three four five six seven | awk '{for (i=3; i<=5; i++) $i="TEST"}1'
one two TEST TEST TEST six seven
echo one two three four five six seven |
    awk -v beg=3 -v end=5 '{for (i=beg; i<=end; i++) $i="TEST"}1'
one two TEST TEST TEST six seven
$ echo "one two three four fix six" | \
sed -E ':r s/(^|(TEST )+)[^ ]*/\1TEST/;/^(TEST ){4}/!br'
TEST TEST TEST TEST fix six