sed 或 Perl 一行 + 如何仅在完全匹配时替换文件中的路径

Posted

技术标签:

【中文标题】sed 或 Perl 一行 + 如何仅在完全匹配时替换文件中的路径【英文标题】:sed or Perl one liner + how to replace path in file only when full match 【发布时间】:2021-08-30 09:39:25 【问题描述】:

我们想替换 /etc/fstab 文件中的路径

/dev/sdb /var/kafka ext4 defaults,noatime 0 0

到(预期输出)

/dev/sdb /var/kafka/hadoop_kafka ext4 defaults,noatime 0 0

我们写的sed语法是

sed s'/\/var\/kafka/\/var\/kafka\/hadoop_kafka/g' /etc/fstab

所以一旦我们运行,我们就会进入 fstab 这个

/dev/sdb /var/kafka/hadoop_kafka ext4 defaults,noatime 0 0

但是一旦我们再次运行 sed,我们就会在 fstab 中得到错误的路径:

/dev/sdb /var/kafka/hadoop_kafka/hadoop_kafka ext4 defaults,noatime 0 0

那么如何替换路径,只有在我们匹配路径的情况下:

/var/kafka

【问题讨论】:

【参考方案1】:

下面的“awk”可以在这里为您提供帮助

$ awk '($2=="/var/kafka")$2="/var/kafka/hadoop_kafka"1' file

awk 的操作方式很简单,另一方面,它的语法对于初学者来说可能有点不寻常。 awk 对记录进行操作,默认情况下是行,每条记录在字段中拆分。默认情况下,这些字段由一个或多个空格(空格、制表符、...)的序列分隔。对于每条记录,awk 将处理以(pattern)action 形式编写的一系列模式-动作对。您可以非常简单地阅读此内容如果pattern 为真,则执行action。默认模式为true,默认操作为print

当我们查看上面的内容时,我们会看到以下两个模式-动作对:

($2=="/var/kafka")$2="/var/kafka/hadoop_kafka"。这我们可以翻译为:如果第二个字段等于“/var/kafka”,则将第二个字段替换为“/var/kafka/hadoop_kafka” 1:这只是说一个,等于“真”并且是一个模式,所以我们添加默认操作print。所以这个语句只打印当前行。

【讨论】:

目标是使用 sed 或 perl ,在 awk 中我需要将输出重定向到另一个文件,然后将文件复制回 fstab 如果您有最新版本的 GNU awk,则可以使用awk -i inplace '(...)...' file。请注意,就地永远不会就地。它只是与您在上面描述的相同,但在内部。 @Judy 不,你可以使用sponge: awk ... file | sponge file @Judy 如果你有 -i 的 GNU sed 或 perl,那么你有或可以得到 GNU awk 对应的 -i inplace。不要仅仅为了伪就地编辑的语法糖而损害您使用的工具/脚本。只需看看 awk 脚本与 sed 等效脚本相比有多简单,并特别注意您不需要转义字符或担心将来任何字符串中的其他正则表达式元字符或子字符串匹配或反向引用,因为 awk 脚本是只需用您关心的字段进行文字字符串比较/替换。【参考方案2】:

使用您显示的示例/尝试,请尝试关注sed 程序。

sed -E 's/^([^ ]*\s+)(\/var\/kafka)(\s.*)/\1\2\/hadoop_kafka\3/ Input_file

说明: 首先使用sed-E 选项启用ERE(扩展正则表达式),然后使用s 选项在此处执行替换。在替换部分正则表达式中使用反向引用概念(将匹配的值保存在临时缓冲存储器中,稍后在程序中使用)。

^([^ ]*\s+):匹配从第一次出现的空间到第一个捕获组中的所有内容。 (\/var\/kafka):创建第二个捕获组,确保它与其中的 /var/kafka 匹配。 (\s.*): 此处匹配其余行。

在执行替换时,根据要求在第二个捕获组之后添加\/hadoop_kafka

注意:上述代码将在终端上打印值,一旦您对结果感到满意,请使用-i 选项将就地保存到 Input_file。

【讨论】:

如果我这样做 --> sed -i -E 's/^([^ ]*\s+)(\/var\/kafka)(.*)/\1\2\/ hadoop_kafka\3/' /etc/fstab ,两次,然后我们得到错误的路径(我们有 rhel linux 7.2) @Judy,我希望您使用的是sed,您的评论表明您使用的是ed,它本身就是一个不同的工具,请确认一次。 @Judy,好的,请告诉我您看到错误输出的示例行吗?对于显示的示例,它对我来说效果很好。 这是我们运行两次语法时得到的结果 --> dev/sdb /var/kafka/hadoop_kafka/hadoop_kafka ext4 defaults,noatime 0 0 是的,这是正确的语法 --> sed -i -E 's/^([^ ]*\s+)(\/var\/kafka)(\s.*)/\1 \2\/hadoop_kafka\3/'【参考方案3】:

你可以试试这个sed:

sed -i.bak -E 's~(^|[[:blank:]])/var/kafka([[:blank:]])~\1/var/kafka/hadoop_kafka\2~g' /etc/fstab

正则表达式详细信息:

(^|[[:blank:]]): 在捕获组 #1 中匹配开始或空格或制表符 /var/kafka:匹配文本/var/kafka ([[:blank:]]): 匹配捕获组 #2 中的空格或制表符 \1/var/kafka/hadoop_kafka\2:替换放回第 1 组中捕获的值,然后是 /var/kafka/hadoop_kafka,然后是第 2 组中捕获的值

【讨论】:

【参考方案4】:
perl -pe's^\S+\s+/var/kafka\K(?=\s)/hadoop_kafka'

你会如何使用它:

perl -i -pe's^\S+\s+/var/kafka\K(?=\s)/hadoop_kafka' /etc/fstab

您想替换第二个字段,它位于非空白的前导序列之后的空白之间。

s
   ^ ( \S+ \s+ ) /var/kafka ( \s | $ )

   $1 . "/var/kafka/hadoop_kafka" . $2
ex

所以,你想要

perl -pe'
   s
      ^ ( \S+ \s+ ) /var/kafka ( \s | $ )
   
      $1 . "/var/kafka/hadoop_kafka" . $2
   ex
'

或同等的

perl -pe's^\S+\s+/var/kafka\K(?=\s)/hadoop_kafka'

【讨论】:

以上是关于sed 或 Perl 一行 + 如何仅在完全匹配时替换文件中的路径的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 sed 在一行中匹配一个或另一个单词

使用 sed 或 awk 按照匹配模式打印一行

Linux三剑客(sed)-编辑匹配到的文本

sed匹配全行,行首,行尾后替换或添加字符

shell匹配字符串,一行中出现多次(grep或sed)

sed