如何使用 sed 和/或 awk 替换字符串中出现的前 n 次?

如何使用 sed 和/或 awk 替换字符串中出现的前 n 次?

我得到一个文件alphabeta其中一行有多个出现的情况。

$ cat alphabet
a b c d e f g 
h i j k a a l
m n a p q r a
s t u v w a x
y z a k l q z

在哪里

$ cat alphabet | grep -o a | wc -l
7

现在我怎样才能替换前 3 次出现的内容,a以便Z我的文件如下所示

Z b c d e f g 
h i j k Z Z l
m n a p q r a
s t u v w a x
y z a k l q z

答案1

awk '{
    for (i=1; i<=NF; i++) 
        if ($i == "a" && n < 3) {
            n++
            $i = "Z"
        }
    print
}' alphabet

或者,“一行”

awk '{for (i=1;i<=NF;i++) if ($i=="a" && n++<3) $i="Z"; print}' alphabet

答案2

Perl 来救援:

perl -pe '$c++ while $c < 3 && s/a/Z/' alphabet

答案3

awk已发布的解决方案假设所有出现的都是a单独的单词。虽然这适用于例子数据,但并未指定真实数据是否正确。以下解决方案更符合已发布的解决方案awk的精神:perl

awk '{ while (changes < 3  &&  sub("a", "Z") > 0) changes++; print }' alphabet

这将取代( 当然,要真正改变文件,你需要做类似的a事情Zchanges

awk '{while (c < 3 && sub("a","Z")>0) c++; print}' alphabet > t && cp t alphabet && rm t

t临时文件在哪里。

答案4

这里,sed 方式

sed -E ':a;N;$!ba;s#a#Z#;s#a#Z#;s#a#Z#' alphabet

由于 sed 通常按行工作,因此任何 sed 命令每次都只能作用于一行。为了能够只替换前 3 次出现的内容,我们需要首先将整个文件设为一个单一选择,然后对它进行 3 次替换。否则,我们将对每一行进行 3 次替换。

  • :a创建标签
  • N将下一行添加到模式空间
  • $!跳过最后一个换行符
  • ba要标记的分支a

现在,我们选择了整个文件,并将对该空间进行操作,而不是一次对一行进行操作,将“a”替换为“Z”。

上述命令只适用于 GNU sed,更通用但有点丑陋的版本应该适用于非 GNU sed:

sed -e ':a' -e 'N' -e '$!ba' -e 's#a#Z#' -e 's#a#Z#' -e 's#a#Z#' alphabet

编辑:如评论中所建议,添加使用 g 命令的版本,首先将所有出现的“a”替换为“Z”,然后将第 3 次之后的所有出现的“Z”再次替换为“a”,这实际上会导致仅替换前 3 次出现的“a”。这样,您可以更改最后一个数字以反映所需的替换次数。

sed -e ':a;N;$!ba;s#a#Z#g;s#Z#a#g4' alphabet

相关内容