我阅读了下面的文章"使用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/" {} +
注意:需要特定于平台的命令的唯一原因是GNUsed
和BSD
sed解释了非标准-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分隔的文件名列表。这样即使文件名中包含空格、换行符或其他困难字符,也可以确保管道的安全。