最佳方法是什么?我不是命令行专家,但我认为可能可以使用 grep
和 cat
的方法。
我只想替换在文件夹及其子文件夹中出现的字符串。如果有影响的话,我正在运行 ubuntu。
我来举个例子,对于使用 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'
ag -l "text" | xargs -I FILE sed -i 's/from/to/g' FILE
- Dmitry-i[SUFFIX],--in-place[=SUFFIX]:原地编辑文件(如果提供了SUFFIX,则进行备份)
。这意味着应该这样写:ag -l "search string" | xargs sed -i -e 's/from/to/g'
,在 -i
后面不加值,否则我会得到与 @Lance 相同的错误。 - Bastien Adamfind . -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'
-i''
,否则它会将-e
解释为后缀并重命名所有文件。完整示例:find . -type f -name '*.js' -print0 | xargs -0 -n 1 sed -i '' -e 's/from/to/g'
(替换所有JavaScript文件中的内容)。 - Thorsten Lorenz正如Paul所说,你首先需要找到想要编辑的文件,然后再进行编辑。使用GNU grep(Ubuntu默认使用)也是使用find的另一种选择,例如:
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
使用Perl是替代sed的一种选择,它可以每个命令处理多个文件,例如:
grep -r -l from . | xargs perl -pi.bak -e 's/from/to/g'
这里告诉Perl进行原地编辑,先创建.bak文件。
你可以根据个人偏好将管道的左侧和右侧组合在一起。
--print0
选项,以使用null作为文件名之间的分隔符,因此您的ack命令将如下所示:ack -l --print0 --text from | xargs -0 -n 1 sed -i -e 's/from/to/g'
- Josh Straterack
管道传输到perl
中,则可以在两者中使用相同的“from”子句。 - Lloeki为了方便起见,我将Ulysse's answer(在纠正不良错误的打印后)转换为.zshrc / .bashrc函数:
function find-and-replace() {
ag -l "$1" | xargs sed -i -e s/"$1"/"$2"/g
}
查找和替换 Foo Bar
替代 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"
,但我担心在去掉单引号后参数扩展的问题。 - Connor典型的(find|grep|ack|ag|rg)-xargs-sed
组合存在一些问题:
xargs -r
选项将在未找到文件时运行命令,可能会导致问题。这些问题足以针对递归搜索和替换这样的侵入性和危险操作开始开发专用工具:mo。
早期测试似乎表明其性能介于ag和rg之间,并解决了我遇到的以下问题:
单个调用可以过滤文件名和内容。以下命令在所有具有v1指示的源文件中搜索单词bug:
mo -f 'src/.*v1.*' -p bug -w
一旦搜索结果正确,就可以添加实际替换bug为fix:
mo -f 'src/.*v1.*' -p bug -w -r fix
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/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'
}
from=str1 to=sth agr path1 path2 ...
不指定路径将使用当前目录。 请注意,需要安装并添加到PATH中的工具有ag、xargs和perl。