我怎样才能发现忽略.svn目录?


227

我经常使用该find命令搜索源代码,删除文件等等。令人讨厌的是,由于Subversion在每个文件的.svn/text-base/目录中存储了每个文件的重复项,因此我的简单搜索最终会得到很多重复的结果。例如,我要递归搜索uint多个messages.hmessages.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客户端升级到1.7版,则不再是问题。

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


4
为了提高性能,请尝试使用find ... -print0 | xargs -0 egrep ...而不是find ... -exec grep ...(不是grep对每个文件进行分叉,而是一次处理一堆文件)。使用此表格,您也可以在.svn不使用-prunefind选项的情况下修剪目录,即find ... -print0 | egrep -v '/\.svn' | xargs -0 egrep ...
vladr 2010年

3
@Vlad:据我所知,使用-execwith +不会grep为每个文件派生,而使用它;会。使用-exec实际上比使用更正确xargs。请注意,ls即使参数列表为空,诸如此类的命令也会执行某些操作;而chmod如果参数不足,则此类命令会给出错误。要了解我的意思,只需在没有任何shell脚本的目录中尝试以下命令:find /path/to/dir -name '*.sh' -print0 | xargs -0 chmod 755。与这个比较:find /path/to/dir -name '*.sh' -exec chmod 755 '{}' '+'
小清庞-明日香健治

2
@Vlad:另外,- grep出去.svn也不是一个好主意。虽然find专门处理文件属性,grep但没有。在您的示例中,名为“ .svn.txt”的文件也会被您的egrep命令过滤。尽管您可以将正则表达式修改为'^ / \。svn $',但这仍然不是一个好习惯。该-prune谓词find完全适用于过滤文件(按文件名,或者创建时间戳,或任何条件你提供)。这就像即使您可以用大剑杀死蟑螂也并不意味着这是建议的方法:-)。
小清庞-明日香健治

3
切换到Git解决了这个问题(还有许多其他问题)。In仅在工作副本的根目录上创建一个.git文件夹,而不是像SVN一样在其每个文件夹中创建一个。此外,.git文件夹不包含纯文件,这些纯文件将与相同名称的实际文件混淆。
Tronic

3
2 Dan Moulding:svn 1.7仅创建一个顶级.svn目录
ccpizza 2011年

Answers:


65

对于搜索,我可以建议您看看ack吗?它是源代码感知的find,因此将自动忽略许多文件类型,包括上述的源代码存储库信息。


3
ack非常喜欢,但是我发现它比find -type f -name "*.[ch]" | xargs grep处理大型代码库要慢得多。
约翰·莱德贝特

63
约翰,我是ack的作者,如果您能给我详细介绍ack vs. grep的速度问题,我将不胜感激。在我发现的所有情况下,它们都是完全可比的。请在github.com/petdance/ack/issues上让我知道,或者在petyance.com上的安迪(Andy)中给我发电子邮件。坦斯克
安迪·莱斯特

63
伙计们,多数民众赞成在小费,但绝对不是问题的答案!:)
dolzenko

8
难道不是ack被称为更好的grep,不是来源感知的find吗?使用它替换的一些示例find将使这个问题成为现实。
michiakig

3
这是对他不知道自己在问的问题的答案。=)
弗鲁吉(Frungi

293

为什么不只是

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

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

所以在你的情况下

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

5
“ -not”和“ -iwholename”的超级大+1。Ack很棒,我使用它,但是find / exec仍然有它的用途。
David Blevins

9
唯一能回答原始问题的答案。
布伦登·克劳福德

14
我超出了我的理解范围,并且我肯定会因为此评论而受到批评,但是-not和-wholename显然不符合POSIX。我用了 !代替-not和-path代替-iwholename,得到相同的结果。根据我的手册页(Ubuntu 12.04),此语法符合POSIX。
约翰·

1
@whaley您'*.svn*'最初说过,但后来说了'*.svn'。哪个是对的?两者都起作用吗?我认为应该是 '*.svn*'
基思M

1
@KeithM实际上很不错。这个答案已经坐在这里多年了,我认为直到现在都没有人知道。
whaley

141

如下:

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

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

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

14
@Kaleb:嗨。我建议,find . -type d -name .svn -prune -o -print因为它快一点。根据POSIX标准,按指定的顺序对表达式进行一次求值。如果in中的第一个表达式-afalse,则将不评估第二个表达式(也称为短路和评估)。
小清庞-明日香健治

2
@Kaleb:比较文件类型(等效于测试是否将某个位设置为整数)比比较文件名(等效于字符串比较,即O(n))要,因此从理论上讲,放置在更高效。但是,除非您有非常大的目录树,否则它通常无关紧要。-type d-name .svn
小清庞-明日香贤治

5
@ SiuChingPong-AsukaKenji-不,仅比较文件名会更快,因为-type需要对每个文件进行stat(2)调用。但是,文件名是readdir(3)响应的一部分。
hraban 2015年

3
@JonathanHartley -print在最后一个表达式中,您缺少。像find . -name .git -prune -o \( -type f -name LICENSE -print \)预期的那样工作。
sschuberth '16

1
如果要同时忽略.git和.svn并仅列出其他目录,则find . -name .svn -prune -o -name .git -prune -o -type d -print。将-type d两者放在前面可能要快几毫秒-name,但不值得额外键入。
JPaget

34

要忽略.svn.git和其他隐藏的目录(从一个点),试一下:

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

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

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

相关:如何在Linux上找到所有包含特定文本的文件?


最佳答案imo。其他人则试图解释无法回答简单问题的事物。
安东尼

19

在您的情况下,这就是我要做的:

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为您的特定问题替换和模式。


12

GNU查找

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

我正在将目录路径加载到数组中以供PHP处理。更高级别的其他答案(无论出于何种原因)并未过滤查找中的文件(尽管-type d)-该答案确实如此。+1
是hollenbeck 2011年

11

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


1
值得注意的是,“ grep”仅在一两年前获得了“ --exclude-dir”选项。最近的Linux发行版都包含它,但是如果我没有记错的话,我必须在OSX上编译自己的grep(或要求自制软件)。
乔纳森·哈特利

我使用一个较小的变体。我的.bashrc创建了一个Bash函数'grp',其定义为GREP_OPTIONS=xxx grep "$@"。这意味着GREP_OPTIONS变量仅为我使用'grp'手动运行的grep实例设置。这意味着我永远不会遇到运行工具的情况,并且在内部它会调用grep,但是该工具会感到困惑,因为grep的行为不符合预期。另外,我还有第二个函数“ grpy”,它调用“ grp”,但是添加了--include=*.py,仅用于搜索Python文件。
乔纳森·哈特利

实际上,经过反思,以我的方式进行操作完全不需要使用GREP_OPTIONS。我现在只有一个shell函数'grp',它会调用grep --exclude=tags --exclude_dir=.git ...etc... "$@"。我喜欢它像“ ack”一样运行,但是我仍然了解并控制它在做什么。
乔纳森·哈特利

9

find . | grep -v \.svn


您必须.在正则.svn表达式中转义。
vladr

4
在grep:或`|中使用--fixed-strings | fgrep -v /.svn/grep -F -v /.svn/以完全排除目录,而不排除名称中带有“ .svn”的文件。
斯蒂芬·P

8

为什么不使用容易理解的grep来传递命令:

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

您必须.在正则.svn表达式中转义。
vladr

@Yclian毫无疑问;如果您不这样做,则从“。”开始还将忽略名为“ tsvn”,“ 1svn”,“ asvn”等的目录。是正则表达式通配符:“匹配任何字符”。
vladr

好吧,我认为这只会在-E和-G的情况下发生。我刚刚测试过,我的不好。:(
yclian

2
我喜欢这个答案,因为它在概念上比其他所有答案都要简单。我不记得“查找”用法的荒谬语法,但是我绝对记得如何使用grep -v,因为它在很多情况下都使用过。
mattismyname 2015年

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;

此脚本无法正常运行。当使用“ svnfind -type f”运行它时,它还会打印svn目录和svn目录中的文件
Ingo Fischer

@ifischer可以echo在find命令中添加一个并告诉我执行了什么命令吗?svnfind -type f在我的Red Hat机器上工作得很好。
约翰·库格曼

好的,所以这似乎取决于操作系统。我正在运行Debian Squeeze(在Ubuntu上也是如此)。我不明白您所说的“添加回声”是什么意思?
Ingo Fischer

@ifischer将最后一行更改为,echo find "${OPTIONS[@]}"...以便它打印find命令,而不是实际运行它。
约翰·库格曼

好,将最后一行更改为echo find ${OPTIONS[@]} ${PATHS[@]} -name .svn -type d -prune -o ( ${EXPR[@]} ) $ACTION,这将为我提供以下输出:find -type f -name .svn -type d -prune -o ( -true ) -print
Ingo Fischer

5

只是以为我会为Kaleb和其他人的帖子(其中详细介绍了option 、、command等的用法)添加一个简单的替代方法,该方法特别适用于您在问题中描述的用法(以及任何其他类似的用法):find -pruneackrepofind

  1. 出于性能考虑,你应该总是尝试使用find ... -exec grep ... +(感谢贤治指出了这一点)或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-prune逻辑。)

    find | grep | xargs方法类似于GNU find-regex选项(请参阅ghostdog74参考资料),但更可移植(也可在GNU find不可用的平台上使用。)


1
@Vlad:请注意,-exec切换有两种形式find:一种以结束,;另一种以结束+。以结尾的所有列表+替换{}为所有匹配文件的列表。此外,您的正则表达式也可以'/\.svn'匹配文件名'.svn.txt'。请参阅我对问题的评论以获取更多信息。
小清庞-明日香贤治

2
@Vlad:find实用程序的POSIX标准。请参阅-exec部分:-)。
小清庞-明日香贤治

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

3

我将find与-not -path选项一起使用。我没有西梅的好运。

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

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


3

要解决此问题,您可以简单地使用以下查找条件:

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

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

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

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


3

请注意,如果您这样做

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

那么-print当整个表达式(-type f -name 'messages.*')为真时就隐含了,因为没有“动作”(例如-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 {}对于不在.svn目录中的文件。

请注意,由于.svn可能总是目录(而不是文件),并且在这种情况下肯定与名称“ message。*”不匹配,因此您最好省略-type dand来做:

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

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

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

那么-print就暗含了该操作,但该操作将应用于WHOLE表达式,包括该-name '.svn' -prune -o部分,从而打印所有.svn目录以及“ message。*”文件,这可能不是您想要的。因此,-prune以这种方式使用时,始终应在布尔表达式的右侧使用“操作” 。在打印该动作时,您必须显式添加它,如下所示:

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


2

尝试使用findrepo,它是围绕find / grep的简单包装,并且比ack快得多。在这种情况下,您将使用它,例如:

findrepo uint 'messages.*'


1

这在Unix提示符下对我有用

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

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


'gfind'是一个错字?我在Ubuntu 14.04上没有它。
乔纳森·哈特利

假设您的意思是“查找”,那么这是行不通的。它还会过滤掉像这样的文件xxx.svnxxx。这很重要-例如,如果您使用的是git而不是svn,则通常会在find结果中包括.gitignore之类的文件(这不是元数据,它是包含在回购中的常规文件)。
乔纳森·哈特利

1

我通常通过grep将输出通过管道传输一次,以删除.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'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.