[英]How can I get `find` to ignore .svn directories?
我经常使用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 对该工作副本所需的所有元数据。
为什么不只是
find . -not -iwholename '*.svn*'
-not 谓词否定路径中任何地方具有 .svn 的所有内容。
所以在你的情况下它会是
find -not -iwholename '*.svn*' -name 'messages.*' -exec grep -Iw uint {} + \;
如下:
find . -path '*/.svn*' -prune -o -print
或者,或者基于目录而不是路径前缀:
find . -name .svn -a -type d -prune -o -print
对于搜索,我可以建议您查看ack吗? 它是一个源代码感知find
,因此会自动忽略许多文件类型,包括源代码存储库信息,如上述。
要忽略.svn
、 .git
和其他隐藏目录(以点开头),请尝试:
find . -type f -not -path '*/\.*'
但是,如果使用find
的目的是在文件中搜索,您可以尝试使用以下命令:
git grep
- 专门设计的用于在 Git 存储库中搜索模式的命令。ripgrep
- 默认情况下忽略隐藏文件和.gitignore
指定的文件。在你的情况下,我会这样做:
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
它忽略由大多数版本控制系统创建的目录,以及为许多编程语言生成的文件。 您可以创建一个别名来调用此命令并针对您的特定问题替换find
和grep
模式。
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 在调用时自动使用这些选项
创建一个名为~/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 来管理您的命令:
your find command| grep -v '\.svn'
只是想我会为 Kaleb 和其他人的帖子添加一个简单的替代方案(其中详细介绍了find -prune
选项、 ack
、 repofind
命令等的使用),这特别适用于您在问题中描述的用法(以及任何其他类似用法):
为了提高性能,您应该始终尝试使用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 | xargs
表单不会为每个文件分叉egrep
,而是一次为一堆文件分叉,从而加快执行速度。
当使用find | xargs
find | xargs
形式,您还可以使用grep
轻松快速地修剪.svn
(或任何目录或正则表达式),即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
我将 find 与 -not -path 选项一起使用。 我的西梅没有好运。
find . -name "*.groovy" -not -path "./target/*" -print
将找到不在目标目录路径中的 groovy 文件。
要解决这个问题,你可以简单地使用这个查找条件:
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
请注意,如果您这样做
find . -type f -name 'messages.*'
当整个表达式( -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 {}
对于不在 .svn 目录中的文件,只会评估-o
正确之处,即-name 'message.*' -exec grep -Iw uint {}
。
请注意,因为.svn
可能始终是一个目录(而不是例如文件),并且在这种情况下肯定与名称“message.*”不匹配,您不妨省略-type d
并执行以下操作:
find . -name '.svn' -prune -o -name 'message.*' -exec grep -Iw uint {}
最后,请注意,如果您省略任何操作( -exec
是一个操作),请像这样说:
find . -name '.svn' -prune -o -name 'message.*'
那么-print
操作是隐含的,但将应用于整个表达式,包括-name '.svn' -prune -o
部分,从而打印所有 .svn 目录以及 'message.*' 文件,这可能不是你想要什么。 因此,以这种方式使用-prune
,您始终应该在布尔表达式的右侧使用“操作”。 当该操作正在打印时,您必须明确添加它,如下所示:
find . -name '.svn' -prune -o -name 'message.*' -print
试试findrepo ,它是一个简单的 find/grep 包装器,比 ack 快得多,你可以在这种情况下使用它,例如:
findrepo uint 'messages.*'
wcfind
是一个查找包装脚本,我用来自动删除 .svn 目录。
我通常再通过 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'
这在 Unix 提示符下对我有用
查找。 \\( -not -wholename '*\\.svn*' \\) -type f -name 'messages.*' -exec grep -Iw uint {} +
上面的命令将列出不包含 .svn 的文件并执行您提到的 grep。
我经常使用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中引入的更改的关键功能是将工作副本元数据存储集中到一个位置。 取而代之的是的
.svn
在工作拷贝的每个目录的目录时,Subversion 1.7的工作拷贝只有一个.svn
目录中的工作拷贝的根。 该目录(除其他外)包括一个由SQLite支持的数据库,该数据库包含该工作副本所需的所有Subversion元数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.