【发布时间】:2011-02-06 07:11:00
【问题描述】:
我找到了这个命令行搜索和替换示例:
find . -type f -print0 | xargs -0 sed -i 's/find/replace/g'
它运行良好,只是它更改了它搜索的每个文件的日期和文件所有权,即使是那些不包含搜索文本的文件。
这个任务有什么更好的解决方案?
【问题讨论】:
我找到了这个命令行搜索和替换示例:
find . -type f -print0 | xargs -0 sed -i 's/find/replace/g'
它运行良好,只是它更改了它搜索的每个文件的日期和文件所有权,即使是那些不包含搜索文本的文件。
这个任务有什么更好的解决方案?
【问题讨论】:
使用-c 选项(如果您在Linux 上)应该会导致sed 保留所有权。当您使用该命令时,它实际上是在重写每个文件,即使它没有对不包含find 的文件进行更改。
【讨论】:
sed 的哪些版本有-c?我的 GNU sed 4.2.1 没有。
-c 作为选项,sed --version 报告 GNU sed version 4.1.2;同样适用于 CentOS 5 安装,其中sed --version 报告GNU sed version 4.1.5; MacOSX 没有 -c 选项,这就是我指定 Linux 的原因。
-c 确实不在 GNU sed 中,它是 RedHat 添加的补丁(最近添加在这里:bugzilla.redhat.com/show_bug.cgi?id=566455,更新日志在这里:rpmfind.net/linux/RPM/centos/7.2.1511/x86_64/Packages/…)。其他发行版可能也加入了它,但我在 Arch 中没有它,它不在 GNU 的手册页中:gnu.org/software/sed/manual/sed.txt
解决此问题的最简单方法是仅在文件包含文本时才对文件执行 sed,首先使用 grep:
find . -type f | while read file; do
grep -q find $file && sed -i 's/find/replace/g' $file
done
这确实需要读取每个文件两次(在最坏的情况下),所以它可能会慢一点。不过,希望您的操作系统应该将文件保存在其磁盘缓存中,因此您不应该看到太多的减速,因为这个过程肯定是 I/O 密集型的,而不是 CPU 密集型的。
【讨论】:
grep+sed。 sed '/find/s/find/replace/g'
grep 是为了避免将 sed 应用于不需要更改的文件,从而避免更改其修改时间。
一些发行版的sed(即RedHat 和家族)添加了一个-c 选项来完成此操作,在这种情况下请参阅@Isaac 的答案。
但是对于那些使用未打补丁的 GNU sed 的人,我发现的最简单的方法是简单地替换为 perl,它会在可用时重写类似于 sed -c 的文件。以下命令基本等价:
sed -ci 's/find/replace/'
perl -pi -e 's/find/replace/'
不要太激动,做perl -pie;与sed -ie 一样,它不会将其解释为另一个选项,而是将e 作为-i 的参数,并将其用作备份原始文件的后缀。有关详细信息,请参阅perldoc perlrun。
对于更复杂的事情,Perl 的正则表达式解析与 sed(更好,imo)略有不同:一般来说,使用较少的反斜杠。
【讨论】: