使用管道将find命令传递给sed(查找和替换)时出现问题

3

这是我的目前代码,我的目标是在给定的目录中(递归地)查找每个文件,并将“FIND”替换为“REPLACEWITH”,并覆盖这些文件。

FIND='ALEX'
REPLACEWITH='<strong>ALEX</strong>'

DIRECTORY='/some/directory/'

find $DIRECTORY -type f -name "*.html" -print0  |
 LANG=C xargs -0  sed  -i "s|$FIND|$REPLACEWITH|g" 

我收到的错误信息是:
sed: 1: "/some/directory ...": command a expects \ followed by text

1
BashFAQ#21详细介绍了如何在目录结构中进行查找/替换:http://mywiki.wooledge.org/BashFAQ/021 - Charles Duffy
我们在知识库中也有很多(非常多)关于正确操作的问题和答案。使用其中一个已确认正确的答案,是否有必要提出新问题呢? - Charles Duffy
1
请注意,您的匿名化处理产生了奇怪的影响,即您问题中的“command a”不再指代您在此处发布的任何命令的一部分。 - ruakh
另外,作为未来的参考,您不应该使用全大写字母命名您自己的变量;这些名称是保留给 shell 提供和系统变量的(根据 IEEE Std 1003.1 中环境变量文档中描述的约定 - 请参见 http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html 的第四段,记住 shell 变量和环境变量使用相同的命名空间)。 - Charles Duffy
无论如何,按照BashFAQ#21所建议的方式使用awk而不是sed将完全消除任何关于您的查找/替换文本被解析为sed脚本而不是数据的问题,这也是您在这里遇到问题的根源所在。 - Charles Duffy
显示剩余5条评论
2个回答

2
根据BashFAQ#21中所述,您可以使用perl执行搜索和替换操作,而不会将数据视为代码的潜在风险:
in="$FIND" out="$REPLACEWITH" find "$DIRECTORY" -type f -name '*.html' \
  -exec perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' '{}' +

如果您想只包括与FIND字符串匹配的文件,可以告诉find仅将标有grep标志的文件传递给perl
in="$FIND" out="$REPLACEWITH" find "$DIRECTORY" -type f -name '*.html' \
  -exec grep -F -q -e "$FIND" '{}' ';' \
  -exec perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' '{}' +

因为 grep 被用于评估单个文件,所以需要每个文件使用一个 grep 调用,以便可以基于每个文件评估其退出状态;因此,使用效率较低的 -exec ... {} ';' 操作。对于 perl,可以将多个要处理的文件放在一个命令中,因此使用 -exec ... {} +
请注意,fgrep 是面向行的;如果您的 FIND 字符串包含多行,则任何具有这些行之一的文件都将传递给 perl 进行替换。

这似乎非常有效,有没有办法让它替换多行代码块? - glasses
一个选项(虽然相对低效)是使用bash中内置的字符串替换支持。也就是说,类似这样:content=$(<"$filename"); new_content=${content//$FIND/$REPLACEWITH}; if [[ $content != "$new_content" ]]; then printf '%s\n' "$new_content" >"$filename" - Charles Duffy
如果你找不到直接相关的问题,我建议你提一个新的问题。如果你找到了相关的问题,欢迎给我发链接。 :) - Charles Duffy

1
您可以让find直接调用sed,尽管我认为所有文件的修改时间都会受到影响(这可能重要也可能不重要):
find $DIRECTORY -type f -name "*.html" -exec sed -i "s|$FIND|$REPLACEWITH|g" '{}' ';'

1
我不确定直接从find调用sed会如何有所帮助 - 明显的问题是sedFINDREPLACE字符串的一部分视为sed脚本而不是数据。 - Charles Duffy
@CharlesDuffy:一般情况下,是的,这总是一个关注的问题。 - Luis
当然可以——根据OP在问题中发布的数据,您的命令将正常工作,但是问题在于——他们在问题中发布的命令也可以使用该数据正常工作。他们真正的数据,导致错误,是他们根本没有向我们展示的。 - Charles Duffy
@A.Danischewski,你不需要为此执行bash;我在我的答案中已经提供了一个版本,使用单个find命令上的两个-exec(第一个调用grep),具有相同的结果行为:-exec不仅是一个操作,而且也是一个过滤器;如果它返回false,则不会调用后续操作。 - Charles Duffy
@A.Danischewski,sedawkperl一样,并不是bash的一部分 - 尽管它们确实因为符合POSIX规范而获得了一些积分。我很高兴地注意到BashFAQ#21包含了一个awk解决方案,完全避免了数据转换为代码的潜在问题,就像我在我的答案中采用的perl解决方案一样。而导致转义的直接原因可能确实是选择了一个不合适的定界符 - 但也有可能是换行符,仅仅选择另一个定界符并不能解决这个问题。 - Charles Duffy
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接