在我的编辑器中,对多个文件进行搜索和替换很困难。可以使用许多技巧,包括使用 find
、xargs
和 sed
/awk
在多个文件中进行搜索和替换,但我无法找到一种交互式的方法。你知道有什么方法吗?
KISS原则:
vim
:args `ls`
:argdo %s#SEARCH#REPLACE#gec |update
%s后面的第一个字符被用作分隔符。
git ls-files
而不是ls
。 - twink_mlgrep
命令中添加 -le
选项。vim `find . -name '*.c' -exec grep -le '\<junk\>' {} \;`
如果你已经在Vim中,但没有选择要替换的内容的机会,请在末尾添加c
选项以进行交互式替换,并在开头添加bufdo
以遍历每个文件:
:bufdo %s/junk/rubbish/gce
稍后您保存所有的工作:
:bufdo wq!
.bashrc
):最初的回答replace () {
if [ $# -lt 2 ]
then
echo "Recursive, interactive text replacement"
echo "Usage: replace text replacement"
return
fi
vim -u NONE -c ":execute ':argdo %s/$1/$2/gc | update' | :q" $(ag $1 -l)
}
使用方法如下:
最初的回答:
~$ replace some_text some_new_text
ag
进行高级搜索,因为这可能比让vim
完成工作更快,但你可以用任何你喜欢的东西代替它。它还以最大速度调用没有插件的vim
,在完成所有替换后自动退出并返回到shell。"最初的回答"只需使用 Vim。
首先使用 find 命令列出所有需要更改的文件列表,如下所示。
vim `find . -name '*.c' -exec grep junk {} \;`
这将使用所有包含字符串junk
的.c
文件启动vim。现在,在vim中对第一个文件进行更改:
:%s/junk/rubbish/g
然后输入:wEnter:nEnter以进行下一个文件。
现在需要重复编辑过程。如果只有一个替换命令和几个文件,则输入:UpEnter以重复替换命令。
但如果有很多,您应该使用map
创建一对按键宏。第一个宏将执行替换命令,第二个宏将执行w
和n
命令以保存更改并获取下一个文件。
我已经使用vi(及其旧版本)使用了30年。特别是当您将此两部分编码为两个按键宏时,可以非常快速地运行它,因为很容易按住控制键,并连续键入几个字母,例如RY。这比编写完全自动化的脚本所需的时间更短,而且由于这很容易复制,因此您可以在任何正在使用的计算机上执行它。
使用grep、xargs和vi/vim的UNIX方法:
当您想要从grep中排除某些内容时,这将有助于避免输入长命令。
alias ge='grep --exclude=tags --exclude=TAGS --exclude-dir=.git --exclude-dir=build --exclude-dir=Build --exclude-dir=classes --exclude-dir=target--exclude-dir=Libraries --exclude=*.log --exclude=*~ --exclude=*.min.js -rOInE $*'
ge -l 'foo' .
命令:
ge -l 'foo' . | xargs -L 1 -o vim -c '%s/foo/bar/gc'
命令说明:
管道符前部分是重复的搜索命令
在管道符后,我们调用xargs来启动vim编辑每个文件
"-L 1"告诉xargs将命令在每行上启动,而不是将所有行合并为单个命令。如果您希望一次启动vim编辑所有文件,请从xargs中删除“-L 1”。
"-o"告诉xargs使用实际的终端,而不是/dev/null
对于此命令,您有两个选项:
a) 一次性在所有文件上启动vim编辑器
优点:
缺点:
您必须依次切换到每个buffer并重新运行"%s"命令
如果您有很多文件,这可能会使vim消耗大量内存(有时甚至像一个全功能IDE!)
b) 逐个文件启动vim编辑器
优点:
一切都是自动化的。Vim被启动并执行搜索命令
内存占用小
缺点:
我更喜欢b)变体,因为它是自动化的,并且更符合UNIX风格。通过仔细的规划 - 首先检查文件列表,可能在子目录上运行命令以减少范围 - 它非常好用。
一般优点:
可以使用标准的UNIX工具,在几乎所有平台上都可用
使用起来很好
一般缺点:
您必须两次键入搜索表达式
在复杂的搜索/替换表达式上可能会遇到bash转义问题
我无法让Mario的答案起作用,但如果我使用xargs它就可以了。
此外,大多数情况下,当我想在多个文件中进行搜索和替换时,我想将一个字母数字ID从一种东西更改为另一种,例如重命名变量或类等。 为此,添加负回顾和负预测非常有帮助,以禁止在您要替换的内容之前或之后使用单词字符,并且grep需要P标志来使用Perl正则表达式:
vim `find . -name '*.cpp' -o -name '*.hpp' | xargs grep -Ple '(?<!\w)FIND(?!\w)'`
:bufdo %s/FIND/REPLACE/gce
#!/bin/bash
if [[ $# < 2 ]]; then
echo "Usage: search-replace <search> <replace>"
exit 1
fi
search="$1"
replace="$2"
files=`find . -name '*.cpp' -o -name '*.hpp' | xargs grep -Ple "(?<!\w)$search(?!\w)"`
if [[ -z "$files" ]]; then
echo "No matching .cpp or .hpp files."
exit 1
fi
# The stuff surrounding $search is the negative lookahead/lookbehind
vim -c "set hidden | bufdo %s/\(\w\)\@<!$search\(\w\)\@!/$replace/gce" $files
对于Mario的解决方案,仍然没有完美地运行的是,在第一个文件的结尾处它说:
Error detected while processing command line:
E37: No write since last change (add ! to override)
这可以防止搜索和替换流到下一个文件。看起来“set hidden”可以解决这个问题,所以我在上面的脚本中使用了它。
剩下的一个问题是,在对第一个文件进行搜索和替换之后,它似乎会再次处理该文件,撤消您最初的替换。如果有人知道为什么会发生这种情况,请告诉我。
你可以尝试使用这个工具在交互模式下在shell中进行查找和替换
https://sourceforge.net/projects/isartool/
不需要X服务器,如果需要可以使用-r选项进行目录递归。
Ciao
如果你经常需要这样做,或者只想搜索版本控制下的文件,这里是对@jhvaras答案的改进。
function SvnProjectSubstitute(replacement)
execute "args `grep -sl '" . @/ . "' $(svn --recursive list)`; echo -n ''"
argdo execute "substitute//" . a:replacement . "/gc"
endfunction
command -nargs=1 -complete=file Gsubstitute call SvnProjectSubstitute(<f-args>)
注意:
Gsubstitute
代表“全局替换”。您可以随意命名该命令,但我喜欢这个名称,因为:Gs
在Vim中似乎很符合风格。grep
的-s
是为了掩盖grep
遇到的任何错误。即使grep
找到了好的结果,它也会返回非零的返回代码。示例用法:
/OldClassName
:Gs NewClassName
我喜欢使用之前的搜索模式的一个原因是它允许您通过将光标放在类上,执行*或#,然后执行:Gs
命令来快速为类命名,这样可以避免您输入整个搜索字符串。