我经常使用find命令来搜索源代码、删除文件等等。令人烦恼的是,由于Subversion在.svn/text-base/目录中存储了每个文件的副本,我的简单搜索最终会得到大量重复的结果。例如,我想在多个messages.h和messages.cpp文件中递归搜索uint:
# find -name 'messages.*' -exec grep -Iw uint {} +
./messages.cpp: Log::verbose << "Discarding out of date message: id " << uint(olderMessage.id)
./messages.cpp: Log::verbose << "Added to send queue: " << *message << ": id " << uint(preparedMessage->id)
./messages.cpp: Log::error << "Received message with invalid SHA-1 hash: id " << uint(incomingMessage.id)
./messages.cpp: Log::verbose << "Received " << *message << ": id " << uint(incomingMessage.id)
./messages.cpp: Log::verbose << "Sent message: id " << uint(preparedMessage->id)
./messages.cpp: Log::verbose << "Discarding unsent message: id " << uint(preparedMessage->id)
./messages.cpp: for (uint i = 0; i < 10 && !_stopThreads; ++i) {
./.svn/text-base/messages.cpp.svn-base: Log::verbose << "Discarding out of date message: id " << uint(olderMessage.id)
./.svn/text-base/messages.cpp.svn-base: Log::verbose << "Added to send queue: " << *message << ": id " << uint(preparedMessage->id)
./.svn/text-base/messages.cpp.svn-base: Log::error << "Received message with invalid SHA-1 hash: id " << uint(incomingMessage.id)
./.svn/text-base/messages.cpp.svn-base: Log::verbose << "Received " << *message << ": id " << uint(incomingMessage.id)
./.svn/text-base/messages.cpp.svn-base: Log::verbose << "Sent message: id " << uint(preparedMessage->id)
./.svn/text-base/messages.cpp.svn-base: Log::verbose << "Discarding unsent message: id " << uint(preparedMessage->id)
./.svn/text-base/messages.cpp.svn-base: for (uint i = 0; i < 10 && !_stopThreads; ++i) {
./virus/messages.cpp:void VsMessageProcessor::_progress(const string &fileName, uint scanCount)
./virus/messages.cpp:ProgressMessage::ProgressMessage(const string &fileName, uint scanCount)
./virus/messages.h: void _progress(const std::string &fileName, uint scanCount);
./virus/messages.h: ProgressMessage(const std::string &fileName, uint scanCount);
./virus/messages.h: uint _scanCount;
./virus/.svn/text-base/messages.cpp.svn-base:void VsMessageProcessor::_progress(const string &fileName, uint scanCount)
./virus/.svn/text-base/messages.cpp.svn-base:ProgressMessage::ProgressMessage(const string &fileName, uint scanCount)
./virus/.svn/text-base/messages.h.svn-base: void _progress(const std::string &fileName, uint scanCount);
./virus/.svn/text-base/messages.h.svn-base: ProgressMessage(const std::string &fileName, uint scanCount);
./virus/.svn/text-base/messages.h.svn-base: uint _scanCount;
我怎么能告诉find忽略。svn目录?
更新:如果您将SVN客户端升级到1.7版本,这就不再是一个问题。
Subversion 1.7中引入的更改的一个关键特性是将工作副本元数据存储集中到单个位置。Subversion 1.7工作副本只有一个.svn目录,而不是在工作副本的每个目录中都有一个.svn目录——在工作副本的根目录。这个目录包括一个sqlite支持的数据库,其中包含Subversion工作副本所需的所有元数据。
创建一个名为~/bin/svnfind的脚本:
#!/bin/bash
#
# Attempts to behave identically to a plain `find' command while ignoring .svn/
# directories.
OPTIONS=()
PATHS=()
EXPR=()
while [[ $1 =~ ^-[HLP]+ ]]; do
OPTIONS+=("$1")
shift
done
while [[ $# -gt 0 ]] && ! [[ $1 =~ '^[-(),!]' ]]; do
PATHS+=("$1")
shift
done
# If user's expression contains no action then we'll add the normally-implied
# `-print'.
ACTION=-print
while [[ $# -gt 0 ]]; do
case "$1" in
-delete|-exec|-execdir|-fls|-fprint|-fprint0|-fprintf|-ok|-print|-okdir|-print0|-printf|-prune|-quit|-ls)
ACTION=;;
esac
EXPR+=("$1")
shift
done
if [[ ${#EXPR} -eq 0 ]]; then
EXPR=(-true)
fi
exec -a "$(basename "$0")" find "${OPTIONS[@]}" "${PATHS[@]}" -name .svn -type d -prune -o '(' "${EXPR[@]}" ')' $ACTION
该脚本的行为与普通的find命令相同,但它删除了.svn目录。否则行为是相同的。
例子:
# svnfind -name 'messages.*' -exec grep -Iw uint {} +
./messages.cpp: Log::verbose << "Discarding out of date message: id " << uint(olderMessage.id)
./messages.cpp: Log::verbose << "Added to send queue: " << *message << ": id " << uint(preparedMessage->id)
./messages.cpp: Log::error << "Received message with invalid SHA-1 hash: id " << uint(incomingMessage.id)
./messages.cpp: Log::verbose << "Received " << *message << ": id " << uint(incomingMessage.id)
./messages.cpp: Log::verbose << "Sent message: id " << uint(preparedMessage->id)
./messages.cpp: Log::verbose << "Discarding unsent message: id " << uint(preparedMessage->id)
./messages.cpp: for (uint i = 0; i < 10 && !_stopThreads; ++i) {
./virus/messages.cpp:void VsMessageProcessor::_progress(const string &fileName, uint scanCount)
./virus/messages.cpp:ProgressMessage::ProgressMessage(const string &fileName, uint scanCount)
./virus/messages.h: void _progress(const std::string &fileName, uint scanCount);
./virus/messages.h: ProgressMessage(const std::string &fileName, uint scanCount);
./virus/messages.h: uint _scanCount;
我只是想给Kaleb和其他人的帖子(详细介绍了find -prune选项,ack, repofind命令等的使用)添加一个简单的替代方案,这特别适用于你在问题中描述的用法(以及任何其他类似的用法):
For performance, you should always try to use find ... -exec grep ... + (thanks Kenji for pointing this out) or find ... | xargs egrep ... (portable) or find ... -print0 | xargs -0 egrep ... (GNU; works on filenames containing spaces) instead of find ... -exec grep ... \;.
The find ... -exec ... + and find | xargs form does not fork egrep for each file, but rather for a bunch of files at a time, resulting in much faster execution.
When using the find | xargs form you can also use grep to easily and quickly prune .svn (or any directories or regular expression), i.e. find ... -print0 | grep -v '/\.svn' | xargs -0 egrep ... (useful when you need something quick and can't be bothered to remember how to set up find's -prune logic.)
The find | grep | xargs approach is similar to GNU find's -regex option (see ghostdog74's post), but is more portable (will also work on platforms where GNU find is not available.)
如果是你,我会这么做:
find . -path .svn -prune -o -name messages.* -exec grep -Iw uint {} +
Emacs的rgrep内置命令忽略了.svn目录,在执行find | grep时,您可能对许多其他文件不感兴趣。下面是它默认使用的:
find . \( -path \*/SCCS -o -path \*/RCS -o -path \*/CVS -o -path \*/MCVS \
-o -path \*/.svn -o -path \*/.git -o -path \*/.hg -o -path \*/.bzr \
-o -path \*/_MTN -o -path \*/_darcs -o -path \*/\{arch\} \) \
-prune -o \
\( -name .\#\* -o -name \*.o -o -name \*\~ -o -name \*.bin -o -name \*.lbin \
-o -name \*.so -o -name \*.a -o -name \*.ln -o -name \*.blg \
-o -name \*.bbl -o -name \*.elc -o -name \*.lof -o -name \*.glo \
-o -name \*.idx -o -name \*.lot -o -name \*.fmt -o -name \*.tfm \
-o -name \*.class -o -name \*.fas -o -name \*.lib -o -name \*.mem \
-o -name \*.x86f -o -name \*.sparcf -o -name \*.fasl -o -name \*.ufsl \
-o -name \*.fsl -o -name \*.dxl -o -name \*.pfsl -o -name \*.dfsl \
-o -name \*.p64fsl -o -name \*.d64fsl -o -name \*.dx64fsl -o -name \*.lo \
-o -name \*.la -o -name \*.gmo -o -name \*.mo -o -name \*.toc \
-o -name \*.aux -o -name \*.cp -o -name \*.fn -o -name \*.ky \
-o -name \*.pg -o -name \*.tp -o -name \*.vr -o -name \*.cps \
-o -name \*.fns -o -name \*.kys -o -name \*.pgs -o -name \*.tps \
-o -name \*.vrs -o -name \*.pyc -o -name \*.pyo \) \
-prune -o \
-type f \( -name pattern \) -print0 \
| xargs -0 -e grep -i -nH -e regex
它忽略大多数版本控制系统创建的目录,以及许多编程语言生成的文件。
您可以创建一个别名来调用此命令,并针对特定的问题替换find和grep模式。
如果您这样做,请注意
找到。-type f -name 'messages.*'
那么,当整个表达式(-type f -name 'messages.*')为真时,-print将被隐含,因为没有'action'(如-exec)。
然而,为了停止降级到某些目录,您应该使用与这些目录匹配的任何内容,并在后面加上-prune(这是为了停止降级到目录);像这样:
找到。-type d -name '。svn的修剪
对于.svn目录,它的计算结果为True,我们可以在后面加上-o (OR)来使用布尔短路,在此之后,-o之后的内容仅在第一部分为False时进行检查,因此不是.svn目录。换句话说,如下:
找到。-type d -name '。Svn ' -prune -o -name '消息。*' -exec grep -Iw uint {}
将只计算正确的-o,即-name '消息。*' -exec grep -Iw uint{},用于不在.svn目录下的文件。
请注意,因为.svn可能总是一个目录(而不是例如一个文件),在这种情况下肯定不匹配name '消息。*',你可以省略类型d,这样做:
找到。- name”。Svn ' -prune -o -name '消息。*' -exec grep -Iw uint {}
最后,注意如果你省略了任何动作(-exec是一个动作),像这样说:
找到。- name”。Svn ' -prune -o -name '消息。*'
则-print操作是隐含的,但将应用于整个表达式,包括名称'。删除Svn ' -prune -o部分,从而打印所有. Svn目录以及'消息。*'文件,这可能不是你想要的。因此,当以这种方式使用-prune时,你总是应该在布尔表达式的右侧使用一个'action'。当这个动作正在打印时,你必须显式地添加它,像这样:
找到。- name”。Svn ' -prune -o -name '消息。*的安全性