我怎样才能让`find`忽略.svn目录?

我经常使用find命令search源代码,删除文件等等。 令人烦恼的是,由于Subversion在其.svn/text-base/目录下存储了每个文件的重复项,我的简单search最终得到了很多重复的结果。 例如,我想在多个messages.hmessages.cpp文件中recursion地searchuint

 # 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需求。

为了search,我可以build议你看看吗? 这是一个源代码感知的find ,因此会自动忽略许多文件types,包括上面的源代码库信息。

为什么不呢

 find . -not -iwholename '*.svn*' 

-not谓词否定了path中任何位置的.svn。

所以你的情况是这样的

 find -not -iwholename '*.svn' -name 'messages.*' -exec grep -Iw uint {} + \; 

如下:

 find . -path '*/.svn*' -prune -o -print 

或者,也可以基于目录而不是path前缀:

 find . -name .svn -a -type d -prune -o -print 

要忽略.svn.git和其他隐藏目录(以点开头),请尝试:

 find . -type f -not -path '*/\.*' 

以下是我会在你的情况下做的事情:

 find . -path .svn -prune -o -name messages.* -exec grep -Iw uint {} + 

Emacs的rgrep内置命令会忽略.svn目录,以及许多您在执行find | grep时可能不感兴趣的文件find | grep 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 

它忽略了大多数版本控制系统创build的目录,以及许多编程语言生成的文件。 你可以创build一个别名来调用这个命令,并replace你的特定问题的findgrep模式。

GNU发现

 find . ! -regex ".*[/]\.svn[/]?.*" 

我使用grep来达到这个目的。 把这个放在〜/ .bashrc中

 export GREP_OPTIONS="--binary-files=without-match --color=auto --devices=skip --exclude-dir=CVS --exclude-dir=.libs --exclude-dir=.deps --exclude-dir=.svn" 

grep会在调用时自动使用这些选项

创build一个名为~/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; 

find . | grep -v \.svn

为什么不用grep来pipe理你的命令,这很容易理解:

 your find command| grep -v '\.svn' 

只是以为我会添加一个简单的替代 Kaleb的和其他人的post(详细使用find -prune选项, ackrepofind命令等),这是特别适用于你在问题中描述的用法 (和任何其他类似用途):

  1. 对于性能,你应该总是尝试使用find ... -exec grep ... + (感谢Kenji指出这一点)或者find ... | xargs egrep ... find ... | xargs egrep ... (便携式)或find ... -print0 | xargs -0 egrep ... find ... -print0 | xargs -0 egrep ... (GNU;适用于包含空格的文件名) 而不是 find ... -exec grep ... \;

    find ... -exec ... +find | xargs find | xargsforms不会为每个文件分配egrep ,而是一次一堆文件,从而导致执行速度更快

  2. 使用find | xargs find | xargs窗体你也可以使用grep来轻松快速地修剪.svn (或任何目录或正则expression式),即find ... -print0 | grep -v '/\.svn' | xargs -0 egrep ... find ... -print0 | grep -v '/\.svn' | xargs -0 egrep ... find ... -print0 | grep -v '/\.svn' | xargs -0 egrep ... (当你需要一些快速的东西时很有用,而且不会被记住如何设置find-prune逻辑。)

    find | grep | xargs find | grep | xargs find | grep | xargs方法类似于GNU find-regex选项(请参阅ghostdog74的文章),但更便于使用(也可用于GNU find不可用的平台上)。

在源代码库中,我通常只想对文本文件进行操作。

第一行是所有文件,不包括CVS,SVN和GIT存储库文件。

第二行不包括所有的二进制文件。

 find . -not \( -name .svn -prune -o -name .git -prune -o -name CVS -prune \) -type f -print0 | \ xargs -0 file -n | grep -v binary | cut -d ":" -f1 

wcfind是一个查找包装脚本,我用它来自动删除.svn目录。

我用-not -path选项使用find。 修剪我没有好运。

 find . -name "*.groovy" -not -path "./target/*" -print 

会发现不在目标目录path中的groovy文件。

试试findrepo这是一个简单的find / grep包装,比ack快得多你可以在这种情况下使用它:

 findrepo uint 'messages.*' 

这在Unix提示符下适用于我

gfind。 \(-not -wholename'* \。svn *'\)-type f -name'messages。*'-exec grep -Iw uint {} +

上面的命令将列出与.svn不同的文件,并执行你提到的grep。

我通常通过greppipe道输出多一次删除.svn,在我使用它不是很慢。 典型的例子:

 find -name 'messages.*' -exec grep -Iw uint {} + | grep -Ev '.svn|.git|.anythingElseIwannaIgnore' 

要么

 find . -type f -print0 | xargs -0 egrep messages. | grep -Ev '.svn|.git|.anythingElseIwannaIgnore' 

要解决这个问题,你可以简单地使用这个查找条件:

 find \( -name 'messages.*' ! -path "*/.svn/*" \) -exec grep -Iw uint {} + 

您可以添加更多像这样的限制:

 find \( -name 'messages.*' ! -path "*/.svn/*" ! -path "*/CVS/*" \) -exec grep -Iw uint {} + 

你可以在手册页的“运营商”部分find更多的信息: http : //unixhelp.ed.ac.uk/CGI/man-cgi? find

请注意,如果你这样做

find . -type f -name 'messages.*'

那么当整个expression式( -type f -name 'messages.*' )为真时, -print被隐含,因为没有'action'(像-exec )。

当停止下降到某些目录时,应使用与这些目录相匹配的任何内容,并使用-prune (旨在停止降级到目录)。 像这样:

find . -type d -name '.svn' -prune

对于.svn目录,它的计算结果为True ,我们可以通过在-o (OR)之后使用布尔短路,之后在-o仅在第一个部分为False时被检查,因此不是 .svn目录。 换句话说,以下几点:

find . -type d -name '.svn' -prune -o -name 'message.*' -exec grep -Iw uint {}

只会评估-o ,即-name 'message.*' -exec grep -Iw uint {} ,文件-name 'message.*' -exec grep -Iw uint {} .svn目录中。

请注意,因为.svn可能总是一个目录(而不是文件),在这种情况下肯定不会匹配名称'message。*',所以您可以省略-type d并执行以下操作:

find . -name '.svn' -prune -o -name 'message.*' -exec grep -Iw uint {}

最后,请注意,如果您省略任何操作( -exec是一个操作),请这样说:

find . -name '.svn' -prune -o -name 'message.*'

那么隐式的-print操作将被应用于WHOLEexpression式,包括-name '.svn' -prune -o部分,因此可以打印所有的.svn目录以及'message。*'文件,这可能不是你想要什么。 因此,当以这种方式使用-prune ,您总是应该在布尔expression式的右边使用“action”。 而当这个动作正在打印时,你必须明确地添加它,如下所示:

find . -name '.svn' -prune -o -name 'message.*' -print