linux - 我怎樣才能讓`find`忽略.svn目錄?




bash grep (17)

我經常使用find命令搜索源代碼,刪除文件等等。 令人煩惱的是,由於Subversion在其.svn/text-base/目錄中存儲了每個文件的重複項,我的簡單搜索最終得到了大量重複的結果。 例如,我想遞歸搜索多個messages.hmessages.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需求。


Answers

試試findrepo這是find / grep的一個簡單包裝,比ack快得多你可以在這種情況下使用它:

findrepo uint 'messages.*'

為什麼不用grep來管理你的命令,這很容易理解:

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

如下:

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

或者,也可以基於目錄而不是路徑前綴:

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

find . | grep -v \.svn


請注意,如果你這樣做

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 {}

只會評估-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操作將被應用於WHOLE表達式,包括-name '.svn' -prune -o部分,因此可以打印所有的.svn目錄以及'message。*'文件,這可能不是你想要什麼。 因此,當以這種方式使用-prune ,您總是應該在布爾表達式的右側使用'action'。 當這個動作正在打印時,你必須明確地添加它,如下所示:

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


我用-not -path選項使用find。 修剪我沒有好運。

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

會發現不在目標目錄路徑中的groovy文件。


以下是我會在你的情況下做的事情:

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

它忽略了大多數版本控制系統創建的目錄,以及許多編程語言生成的文件。 你可以創建一個別名來調用這個命令,並替換你的特定問題的findgrep模式。


只是認為我會為Kaleb和其他人的帖子(詳細介紹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 | xargs窗體不會為每個文件分配egrep ,而是一次為一堆文件分配,從而導致執行速度更快

  2. 使用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不可用的平台)。


為什麼不只是

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

-not謂詞否定在路徑中任何位置具有.svn的所有內容。

所以你的情況是這樣的

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

這在Unix提示符下適用於我

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

上面的命令將列出不包含.svn的文件,並執行您提到的grep。


要解決這個問題,你可以簡單地使用這個查找條件:

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 : http://unixhelp.ed.ac.uk/CGI/man-cgi?find find


要忽略.svn.git和其他隱藏目錄(以點開頭),請嘗試:

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

創建一個名為~/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;

在源代碼庫中,我通常只想對文本文件進行操作。

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

我通常通過grep通過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'

為了搜索,我可以建議你看看嗎? 這是一個源代碼意識的find ,因此會自動忽略許多文件類型,包括上述的源代碼庫信息。


在Bash中,您還可以明確指定您的重定向到不同的文件:

cmd >log.out 2>log_error.out

追加將是:

cmd >>log.out 2>>log_error.out




linux find bash grep svn