在多个文件中进行查找/替换的最佳方法?

Posted

技术标签:

【中文标题】在多个文件中进行查找/替换的最佳方法?【英文标题】:Best way to do a find/replace in several files? 【发布时间】:2010-10-06 11:21:07 【问题描述】:

最好的方法是什么?我不是命令行战士,但我在想可能有一种使用grepcat 的方法。

我只想替换文件夹和子文件夹中出现的字符串。最好的方法是什么?如果这很重要,我正在运行 ubuntu。

【问题讨论】:

把这个链接放在这里:***.com/questions/102083/… 【参考方案1】:

我将为使用ag, The Silver Searcher 对多个文件执行查找/替换操作的人们提供另一个示例。

完整示例:

ag -l "search string" | xargs sed -i '' -e 's/from/to/g'

如果我们把它分解,我们得到的是:

# returns a list of files containing matching string
ag -l "search string"

接下来,我们有:

# consume the list of piped files and prepare to run foregoing command
# for each file delimited by newline
xargs

最后是字符串替换命令:

# -i '' means edit files in place and the '' means do not create a backup
# -e 's/from/to/g' specifies the command to run, in this case,
# global, search and replace

sed -i '' -e 's/from/to/g'

【讨论】:

这不再适合我。我不断收到错误“sed 无法读取:没有这样的文件或目录” 你应该这样做ag -l "text" | xargs -I FILE sed -i 's/from/to/g' FILE 男人说-i[SUFFIX], --in-place[=SUFFIX]: edit files in place (makes backup if SUFFIX supplied)。这意味着它应该是ag -l "search string" | xargs sed -i -e 's/from/to/g'-i 之后没有价值,否则我得到的错误与@Lance 相同【参考方案2】:
find . -type f -print0 | xargs -0 -n 1 sed -i -e 's/from/to/g'

第一部分是一个查找命令,用于查找要更改的文件。您可能需要适当地修改它。 xargs 命令获取找到的每个文件并将sed 命令应用于它。 sed 命令获取 from 的每个实例并将其替换为 to。这是一个标准的正则表达式,所以根据需要修改它。

如果您使用的是 svn,请注意。您的 .svn 目录也将被搜索和替换。你必须排除那些,例如,像这样:

find . ! -regex ".*[/]\.svn[/]?.*" -type f -print0 | xargs -0 -n 1 sed -i -e 's/from/to/g'

find . -name .svn -prune -o -type f -print0 | xargs -0 -n 1 sed -i -e 's/from/to/g'

【讨论】:

这个命令似乎没有递归到子目录中。是不是我做错了什么? 是什么让你认为它没有递归到目录中? 子目录中的文本文件没有进行更改。我想要它做的是:在所有子目录中将 some.ip.address 的出现更改为 somewebsite.com。 将“sed”替换为“echo”,并确保它可以找到您的所有文件。 我在 Mac (Lion) 上,我必须添加一个空字符串以导致不应用任何后缀,即:-i '',否则它将 -e 解释为后缀和重命名所有文件。完整示例:find . -type f -name '*.js' -print0 | xargs -0 -n 1 sed -i '' -e 's/from/to/g'(替换所有 javascript 文件)。【参考方案3】:

正如 Paul 所说,您要先找到要编辑的文件,然后再进行编辑。使用 find 的替代方法是使用 GNU grep(Ubuntu 上的默认设置),例如:

grep -r -l from . | xargs -0 -n 1 sed -i -e 's/from/to/g'

你也可以使用 ack-grep(sudo apt-get install ack-grep 或访问http://petdance.com/ack/),如果你知道你只想要某种类型的文件,并且想忽略版本控制目录中的东西。例如,如果您只想要文本文件,

ack -l --print0 --text from | xargs -0 -n 1 sed -i -e 's/from/to/g'
# `from` here is an arbitrary commonly occurring keyword

使用 sed 的另一种方法是使用 perl,它可以为每个命令处理多个文件,例如,

grep -r -l from . | xargs perl -pi.bak -e 's/from/to/g'

这里,perl 被告知要就地编辑,首先创建一个 .bak 文件。

您可以根据自己的喜好将管道的任何左侧与右侧组合起来。

【讨论】:

您可以使用“-prune”命令避免“查找”中的版本控制目录。例如“find .-name RCS -prune -o -type f -print0” 是的,虽然它需要更少的脑力来为你处理像 ack 这样的工具。 ack 还接受 --print0 选项以使用 null 作为文件名之间的分隔符,因此您的 ack 命令看起来像这样:ack -l --print0 --text from | xargs -0 -n 1 sed -i -e 's/from/to/g' 谢谢,修复了内联文本。 注意 grep、ack、perl、sed 并非都使用相同的正则表达式变体。 Ack 是用 perl 编写的,如果您将 ack 管道传输到 perl,则可以在两者中使用相同的 from 子句。【参考方案4】:

sed 的替代方法是使用 rpl(例如,可从 http://rpl.sourceforge.net/ 或您的 GNU/Linux 发行版获得),例如 rpl --recursive --verbose --whole-words 'F' 'A' grades/

【讨论】:

您将如何在脚本中使用它?我终于选择了:ag -l "$1" | xargs sed -i '' -e "s/$1/$2/g",但我担心删除单引号后的参数扩展【参考方案5】:

典型的(find|grep|ack|ag|rg)-xargs-sed 组合有几个问题:

很难记住和正确。例如,即使没有找到文件,忘记xargs -r 选项也会运行命令,这可能会导致问题。 检索文件列表,实际替换使用不同的 CLI 工具,并且可能具有不同的搜索行为。

这些问题对于递归搜索和替换这样的侵入性和危险操作来说已经足够大了,以至于开始开发专用工具:mo。

早期测试似乎表明它的性能介于ag 和rg 之间,它解决了我遇到的以下问题:

单个调用可以过滤文件名内容。以下命令在所有具有 v1 指示的源文件中搜索单词 bug

mo -f 'src/.*v1.*' -p bug -w

一旦搜索结果OK,就可以用fix实际替换bug了:

mo -f 'src/.*v1.*' -p bug -w -r fix

【讨论】:

我认为明确提及您是所推广工具的主要作者很重要。【参考方案6】:
comment()  

doc()  

function agr  
doc 'usage: from=sth to=another agr [ag-args]'
comment -l --files-with-matches

ag -0 -l "$from" "$@" | pre-files "$from" "$to"

pre-files() 
doc 'stdin should be null-separated list of files that need replacement; $1 the string to replace, $2 the replacement.'
comment '-i backs up original input files with the supplied extension (leave empty for no backup; needed for in-place replacement.)(do not put whitespace between -i and its arg.)'
comment '-r, --no-run-if-empty
              If  the  standard input does not contain any nonblanks,
              do not run the command.  Normally, the command  is  run
              once  even  if there is no input.  This option is a GNU
              extension.'

AGR_FROM="$1" AGR_TO="$2" xargs -r0 perl -pi.pbak -e 's/$ENVAGR_FROM/$ENVAGR_TO/g'

你可以这样使用它:

from=str1 to=sth agr path1 path2 ...

不提供路径以使其使用当前目录。 请注意,需要在 PATH 上安装 ag、xargs 和 perl。

【讨论】:

以上是关于在多个文件中进行查找/替换的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

在UltraEdit的查找和替换中使用正则表达式 (转)

在多个批处理文件中查找和替换

Linux使用vim进行多文件查找和替换的方法

Perl:在多个文本文件中查找和替换特定字符串

使用 .bat 文件在目录中包含的多个文件中查找和替换字符串

在多个目录中的文件中查找和替换包含引号的字符串 - unix aix