如何使用sed替换字符串中的字符串



我阅读了下面的文章"使用grep和sed查找和替换字符串",但我如何将其扩展到链接多个grep。例如,我有以下目录/文件结构

dir1/metadata.txt
dir2/metadata.txt

dir1/metadata.txt具有

filename1 '= 1.0.0'
filename2 '= 1.0.0'

dir2/metadata.txt具有

filename1     '= 1.0.0'
long_filename '= 1.0.0'

换句话说,dir1/metadata.txt和dir2/metadata.txt都包含"filename'1.0.0'",但每个文件中"filename"one_answers"'1.0.0'"之间的空格不同。

现在,我想在所有metadata.txt文件中将filename1的相关版本替换为"2.0.0",这样得到的文件看起来像。。。

dir1/metadata.txt具有

filename1 '= 2.0.0'
filename2 '= 1.0.0'

dir2/metadata.txt具有

filename1     '= 2.0.0'
long_filename '= 1.0.0'

我正在试用

find . -name metadata.txt | xargs grep filename1 | sed -i "s/1.0.0/2.0.0/g" <some option here>

但我知道"这里有一些选择"的部分。有线索吗?

尝试以下操作:

Linux:

find . -name metadata.txt 
-exec sed -i "s/^(filename1[[:space:]]{1,}'= )1.0.0/12.0.0/" {} +

OSX/BSD:

find . -name metadata.txt 
-exec sed -i '' "s/^(filename1[[:space:]]{1,}'= )1.0.0/12.0.0/" {} +

注意:需要特定于平台的命令的唯一原因是GNUsedBSDsed解释了非标准-i选项,该选项指定了用于原始文件的可选备份后缀,不同之处在于:GNUsed认为-i的选项参数是可选;而BSDsed认为它是强制,需要显式参数来指定空字符串(表示希望而不是创建备份文件)

  • exec ... +是一个find功能,它使用单个命令行上可以容纳的匹配路径来调用指定的命令,可能会导致多个调用,但通常会导致仅1,这使得调用高效

  • "s/(filename1[[:space:]]{1,}'= )1.0.0/12.0.0/"是一个符合POSIX的sed脚本,它在一行的开头匹配文字filename1,后面是可变数量的空白([[:space:]]{1,}),后面是文字'= 1.0.0,并用2.0.0替换1.0.0.

  • 请注意,如果有metadata.txt文件的不是,它们的行以filename1开头,则它们仍被重写,因为sed-i选项会盲目地"更新"给定的输入文件(读取:创建一个新文件,然后替换原始文件)。如果这是不希望的,请考虑John1024的回答。

POSIX合规说明:

  • find-exec初级的-exec ... +变体自2001年以来一直是POSIX的一部分(POSIX.1-2001/IEEE Std 1003.1-2001/SUS v3-请参阅http://pubs.opengroup.org/onlinepubs/009695399/;谢谢,@JonathanLeffler)
  • 相比之下,sed用于就地更新-i选项不符合POSIX,因此您可能必须解决此问题
find . -name metadata.txt -exec grep -l --null filename1 {} + | xargs -0 sed -i "/^filename1 /{s/'= 1.0.0'/'= 2.0.0'/;}"

sed -i将更新它处理的每个文件的时间戳,而不管它是否更改了文件的内容。这是因为,在操作中,sed -i为处理的每个文件创建一个新文件,然后用新文件覆盖旧文件。为了限制这种情况,上面的代码使用grep只选择可能需要修改的文件,并通过管道只将这些文件名发送到sed -i进行更新。

如果时间戳/覆盖问题并不重要,请考虑mklement0的答案,它消除了对管道的需求,简化了命令。

它的工作原理

  • find . -name metadata.txt -exec grep -l --null filename1 {} +

    这将生成名为metadata.txt的文件列表,其中还包含filename

    --null命令grep用NUL字符分隔文件名。

  • xargs -0 sed -i "/^filename1 /{s/'= 1.0.0'/'= 2.0.0'/;}"

    这将应用sed -i就地更改其名称由上述find命令返回的文件。

    更详细地说:

    • /^filename1 /

      这将选择以filename1开头、后跟空格的行。这确保了我们既不匹配sfilename1也不匹配filename12

    • s/'= 1.0.0'/'= 2.0.0'/

      这将更改所选行的版本号。(这假设等号后只有一个空格。如果这个假设不正确,我们可以很容易地更改它。)

    xargs-0选项告诉它希望它的输入是一个NUL分隔的文件名列表。这样即使文件名中包含空格、换行符或其他困难字符,也可以确保管道的安全。

相关内容

  • 没有找到相关文章

最新更新