如何仅替换文件中第 N 次出现的模式?

如何仅替换文件中第 N 次出现的模式?

我怎样才能更换使用命令在文件中第 th(例如,第三次)出现字符串(或模式)  sed?我的意思是文件中的第 次出现,而不是一行中的第 次出现 或 行中的第一次出现 第 条匹配线。一行中可能会多次出现,并且一次出现(匹配)可能是部分单词。

例子:

仅更改文件中第三次出现的isto 。us

我的输入文件包含:

hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged.

预期输出是:

hai this is linux.
hai thus is unix.
hai this is mac.
hai this is unchanged.

请注意,“this”已替换为“th”我们”在第二行。

答案1

用 来完成就容易多了perl

要更改第三次出现的位置:

perl -pe 's{is}{++$n == 3 ? "us" : $&}ge'

每出现 3次就更改一次:

perl -pe 's{is}{++$n % 3 ? $& : "us"}ge'

答案2

sed如果以前的换行符被替换为任何其他字符,则可以使用它,例如:

tr '\n' '\000' | sed 's/is/us/3' | tr '\000' '\n'

与纯(GNU)相同sed

sed ':a;N;$!ba;s/\n/\x0/g;s/is/us/3;s/\x0/\n/g'

sed换行替换无耻地从https://stackoverflow.com/a/1252191/4488514

答案3

当替换字符串每行仅出现一次时,您可以组合不同的实用程序。
当输入位于文件“input”中并且您将“is”替换为“us”时,您可以使用

LINENR=$(cat input | grep -n " is " | head -3 | tail -1 | cut -d: -f1)
cat input | sed ${LINENR}' s/ is / us /'

答案4

p='[:punct:]' s='[:space:]'
sed -Ee'1!{/\n/!b' -e\}            \
     -e's/(\n*)(.*)/ \2 \1/'       \
     -e"s/is[$p]?[$s]/\n&/g"       \
     -e"s/([^$s])\n/\1/g;1G"       \
-e:c -e"s/\ni(.* )\n{3}/u\1/"      \
     -e"/\n$/!s/\n//g;/\ni/G"      \
     -e's//i/;//tc'                \
     -e's/^ (.*) /\1/;P;$d;N;D'

该位sed仅包含is从一行到下一行的出现次数。它应该可靠地处理每行尽可能多的ises,并且不需要缓冲旧行 - 它只是为is遇到的每个不属于另一个单词的部分保留一个换行符。

结果是它只会修改文件中的第三次出现 - 并且每行都会包含计数。因此,如果文件如下所示:

1. is is isis
2. is does

...它将打印...

1. is is isis
2. us does

它首先通过在每行的头部和尾部插入一个空格来处理边缘情况。这使得单词边界更容易确定。

接下来,它通过在紧邻零个或一个标点符号字符(后跟一个空格)的所有出现的 es 之前插入一个 ewline来查找有效的ises 。它执行另一遍操作并删除紧随其后的非空格字符的所有换行符。留下的标记将匹配and但不匹配or 。\nis\nis.isthis?is

接下来,它将每个标记收集到字符串的尾部 - 对于\ni行上的每个匹配,它都会将\newline 附加到字符串的尾部,并将其替换为iu。如果\n连续有 3 个ewlines 聚集在字符串的尾部,那么它使用 u - 否则使用 i。第一次使用 au 也是最后一次 - 替换引发了无限循环,归结为get line, print line, get line, print line,依此类推。

在每个 try 循环周期结束时,它会清除插入的空格,仅打印到模式空间中第一个出现的换行符,然后再次执行。

l我将在循环的开头添加一个ook 命令,例如:

l; s/\ni(.* )\n{9}/u\1/...

...看看它在处理此输入时会做什么:

hai this is linux.
hai this is unix.


hai this is mac.
hai this is unchanged is.

...所以这就是它的作用:

 hai this \nis linux. \n$        #behind the scenes
hai this is linux.               #actually printed
 hai this \nis unix. \n\n$       #it builds the marker string
hai this is unix.
  \n\n\n$                        #only for lines matching the

  \n\n\n$                        #pattern - and not otherwise.

 hai this \nis mac. \n\n\n$      #here's the match - 3 ises so far in file.
hai this us mac.                 #printed
hai this is unchanged is.        #no look here - this line is never evaled

is每行有更多 es 可能更有意义:

nthword()(  p='[:punct:]' s='[:space:]'         
    sed -e '1!{/\n/!b' -e\}             \
        -e 's/\(\n*\)\(.*\)/ \2 \1/'    \
        -e "s/$1[$p]\{0,1\}[$s]/\n&/g"  \
        -e "s/\([^$s]\)\n/\1/g;1G;:c"   \
        -e "${dbg+l;}s/\n$1\(.* \)\n\{$3\}/$2\1/" \
        -e '/\n$/!s/\n//g;/\n'"$1/G"    \
        -e "s//$1/;//tc" -e 's/^ \(.*\) /\1/'     \
        -e 'P;$d;N;D'
)        

这实际上是相同的事情,但是是用 POSIX BRE 和基本参数处理编写的。

 printf 'is is. is? this is%.0s\n' {1..4}  | nthword is us 12

...得到...

is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is

...如果我启用${dbg}

printf 'is is. is? this is%.0s\n' {1..4}  | 
dbg=1 nthword is us 12

...我们可以看着它迭代...

 \nis \nis. \nis? this \nis \n$
 is \nis. \nis? this \nis \n\n$
 is is. \nis? this \nis \n\n\n$
 is is. is? this \nis \n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n\n\n\n\n$
is is. is? this us
is is. is? this is

相关内容