如何让`find`忽略.svn目录?

时间:2010-02-22 22:09:18

标签: linux find bash grep svn

我经常使用find命令搜索源代码,删除文件等等。令人讨厌的是,因为Subversion在其.svn/text-base/目录中存储了每个文件的重复项,所以我的简单搜索最终会得到大量重复的结果。例如,我想在多个uintmessages.h文件中递归搜索messages.cpp

# 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客户端升级到version 1.7,则不再是此问题。

  

Subversion 1.7中引入的更改的一个关键特性是将工作副本元数据存储集中到一个位置。 Subversion 1.7工作副本在工作副本的根目录中只有一个.svn目录,而不是工作副本中每个目录中的.svn目录。该目录包括(除其他外)一个SQLite支持的数据库,该数据库包含Subversion对该工作副本所需的所有元数据。

20 个答案:

答案 0 :(得分:283)

为什么不只是

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

-not谓词否定了路径中任何地方.svn的所有内容。

所以在你的情况下,它将是

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

答案 1 :(得分:140)

如下:

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

或者,或者基于目录而不是路径前缀:

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

答案 2 :(得分:64)

搜索时,我建议您查看ack吗?它是一个源代码识别find,因此会自动忽略许多文件类型,包括上面的源代码存储库信息。

答案 3 :(得分:29)

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

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

但是,如果使用find的目的是在文件中搜索,您可以尝试使用这些命令:

  • git grep - 专门设计的用于在Git存储库中搜索模式的命令。
  • ripgrep - 默认情况下会忽略.gitignore中指定的隐藏文件和文件。

相关:How do I find all files containing specific text on Linux?

答案 4 :(得分:18)

以下是我要处理的事情:

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

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

答案 5 :(得分:12)

GNU find

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

答案 6 :(得分:10)

我为此目的使用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会在调用时自动使用这些选项

答案 7 :(得分:8)

find . | grep -v \.svn

答案 8 :(得分:8)

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

答案 9 :(得分:7)

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

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

答案 10 :(得分:5)

我想我会在Kaleb和其他人的帖子中添加一个简单的替代(其中详细介绍了使用find -prune选项,ackrepofind命令等)特别适用于您在问题中描述的用法(以及任何其他类似的用法):

  1. 为了提高性能,您应该始终尝试使用find ... -exec grep ... +(感谢Kenji指出这一点)或find ... | xargs egrep ...(可移植)或find ... -print0 | xargs -0 egrep ...(GNU;适用于包含文件名的文件名)空格)而不是find ... -exec grep ... \;

    find ... -exec ... +find | xargs表单不会为每个文件分叉egrep,而是一次为一堆文件分叉,从而导致执行快得多

  2. 使用find | xargs表单时,您还可以使用grep轻松快速地修剪.svn(或任何目录或正则表达式),即find ... -print0 | grep -v '/\.svn' | xargs -0 egrep ...(当您需要快速的内容并且无法记住如何设置find的{​​{1}}逻辑时,这非常有用。)

    -prune方法类似于GNU find | grep | xargs的{​​{1}}选项(请参阅find的帖子),但更具可移植性(也适用于GNU -regex不可用。)

答案 11 :(得分:4)

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

第一行是所有文件,不包括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

答案 12 :(得分:3)

我使用-not -path选项查找。我对修剪没有好运。

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

将找到不在目标目录路径中的groovy文件。

答案 13 :(得分:2)

尝试findrepo这是一个简单的查找/ grep包装器,比ack快得多 你会在这种情况下使用它,如:

findrepo uint 'messages.*'

答案 14 :(得分:2)

请注意,如果你这样做

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

然后在整个表达式(-print)为真时隐含-type f -name 'messages.*',因为没有'动作'(如-exec)。

然而,要停止下降到某些目录,您应该使用与这些目录匹配的任何内容,并按-prune跟随它(它旨在停止下降到目录中);像这样:

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

这对.svn目录的 True 进行求值,我们可以通过-o(OR)跟随它来使用布尔短路,然后在{{1仅在第一部分为False时检查,因此不是 .svn目录。换句话说,以下内容:

-o

对于不在.svn目录中的文件,

只会评估find . -type d -name '.svn' -prune -o -name 'message.*' -exec grep -Iw uint {}的正确内容,即-o

请注意,因为-name 'message.*' -exec grep -Iw uint {}可能总是一个目录(而不是例如文件),并且在这种情况下肯定与名称'message。*'不匹配,所以你可能会遗漏{ {1}}并且执行:

.svn

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

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

然后暗示-exec动作,但将应用于WHOLE表达式,包括find . -name '.svn' -prune -o -name 'message.*'部分,从而打印所有.svn目录以及'message。*'文件,这可能是不是你想要的。因此,当以这种方式使用-print时,您总是应该在布尔表达式的右侧使用“动作”。当该动作打印时,您必须明确地添加它,如下所示:

-name '.svn' -prune -o

答案 15 :(得分:2)

要解决此问题,您只需使用此查找条件:

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

您可以添加更多限制:

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

您可以在手册页部分&#34;操作员&#34;中找到有关此内容的更多信息: http://unixhelp.ed.ac.uk/CGI/man-cgi?find

答案 16 :(得分:2)

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

答案 17 :(得分:1)

我通常通过grep管道输出再次删除.svn,在我的使用中它并没有太慢。 典型的例子:

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

OR

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

答案 18 :(得分:1)

这在Unix提示符下适用于我

  

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

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

答案 19 :(得分:0)

如果您告诉find搜索“ *”,那么它将跳过根目录中的所有“点文件”:

find *

或带有路径

find /path/*

这不是该问题的精确/完美解决方案。 但是,没有比这更简单的解决方案了。 性能也非常好,因为它甚至都不会进入隐藏目录。

缺点:

  • 它不处理嵌套的“点文件”。
  • 所有“点文件”都会被忽略,而不仅仅是“ .git”和“ .svn”。

因此,在您的示例中,仅需添加星号即可:

find * -name 'messages.*' -exec grep -Iw uint {} +