Как я могу заставить `find` игнорировать каталоги .svn?

Я часто использую команду find для поиска исходного кода, удаления файлов и т.д. Раздражающе, потому что Subversion хранит дубликаты каждого файла в своих каталогах .svn/text-base/, мои простые поиски заканчиваются тем, что получают много повторяющихся результатов. Например, я хочу рекурсивно искать uint в нескольких файлах messages.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-клиент до версии 1.7, это уже не проблема.

Ключевой особенностью изменений, внесенных в Subversion 1.7, является централизация хранилища метаданных рабочей копии в одном месте. Вместо каталога .svn в каждом каталоге рабочей копии рабочие копии Subversion 1.7 имеют только один каталог .svn - в корне рабочей копии. Этот каталог включает (помимо прочего) базу данных с поддержкой SQLite, которая содержит все потребности Subversion для метаданных для этой рабочей копии.

Ответ 1

Для поиска я могу предложить вам посмотреть ack? Это код исходного кода find, и как таковой автоматически игнорирует многие типы файлов, включая информацию о репозитории исходного кода, такую ​​как приведенная выше.

Ответ 2

почему не просто

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

Не предикат отрицает все, что имеет .svn в любом месте пути.

Итак, в вашем случае это будет

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

Ответ 3

Как следует:

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

Или, альтернативно, на основе каталога, а не префикса пути:

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

Ответ 4

Чтобы игнорировать .svn, .git и другие скрытые каталоги (начиная с точки), попробуйте:

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

Однако, если цель использования find выполняется в файлах, вы можете попробовать использовать следующие команды:

  • git grep - специально разработанная команда для поиска шаблонов в репозитории Git.
  • ripgrep - который по умолчанию игнорирует скрытые файлы и файлы, указанные в .gitignore.

Связано: Как найти все файлы, содержащие определенный текст в Linux?

Ответ 5

Вот что я сделал бы в вашем случае:

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

Он игнорирует каталоги, созданные большинством систем управления версиями, а также сгенерированные файлы для многих языков программирования. Вы можете создать псевдоним, который вызывает эту команду, и заменять шаблоны find и grep для ваших конкретных проблем.

Ответ 6

Найти GNU

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

Ответ 7

Я использую 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 автоматически использует эти параметры при вызове

Ответ 8

Создайте script под названием ~/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 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

Этот script ведет себя одинаково с простой командой 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

find . | grep -v \.svn

Ответ 10

Почему вы не связываете свою команду с grep, что легко понять:

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

Ответ 11

Просто подумал, что добавлю простую альтернативу к сообщениям Kaleb и других пользователей (в которых подробно описываются опции find -prune, ack, repofind и т.д.), что , особенно применимый к использованию, которое вы описали в вопросе (и любых других подобных случаях):

  • Для производительности вы всегда должны использовать find ... -exec grep ... + (спасибо Kenji за указание этого) или find ... | xargs egrep ... (переносимый) или find ... -print0 | xargs -0 egrep ... (GNU; работает с именами файлов, содержащих пробелы) вместо find ... -exec grep ... \;.

    Форма find ... -exec ... + и find | xargs не для fork egrep для каждого файла, а скорее для группы файлов за раз, в результате чего выполняется намного быстрее.

  • При использовании формы find | xargs вы также можете использовать grep, чтобы легко и быстро обрезать .svn (или любые каталоги или регулярные выражения), т.е. find ... -print0 | grep -v '/\.svn' | xargs -0 egrep ... (полезно, когда вам нужно что-то быстрое и может Не надо беспокоиться о том, как настроить логику find -prune.)

    Подход find | grep | xargs похож на вариант GNU find -regex (см. ghostdog74 post), но более переносимый (также будет работать на платформах, где GNU find недоступен.)

Ответ 12

В репозитории исходного кода я вообще хочу делать что-то только в текстовых файлах.

Первая строка - это все файлы, исключая файлы репозитория 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

Ответ 13

Я использую find с параметрами -not -path. Мне не повезло с чернотой.

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

найдет файлы groovy не в пути к целевому каталогу.

Ответ 14

Попробуйте findrepo, который является простой оболочкой find/grep и намного быстрее, чем ack Вы бы использовали его в этом случае, например:

findrepo uint 'messages.*'

Ответ 15

wcfind - это обертка для поиска script, которую я использую для автоматического удаления каталогов .svn.

Ответ 16

Чтобы решить эту проблему, вы можете просто использовать это условие поиска:

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

Вы можете добавить больше ограничений, например:

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

Дополнительную информацию об этом можно найти в разделе "Операторы" на странице man: http://unixhelp.ed.ac.uk/CGI/man-cgi?find

Ответ 17

Обратите внимание, что если вы делаете

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

то -print подразумевается, когда все выражение (-type f -name 'messages.*') истинно, потому что нет "действия" (например, -exec).

В то время как для остановки перехода в определенные каталоги следует использовать все, что соответствует этим каталогам, и следовать за ним с помощью -prune (который предназначен для остановки спуска в директории); так:

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

Это оценивается как True для каталогов .svn, и мы можем использовать логическое короткое замыкание, следуя этому с помощью -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 {}, для файлов NOT внутри каталогов .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 вы всегда должны использовать "действие" в правой части булевского выражения. И когда это действие печатает, вы должны явно добавить его, например:

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

Ответ 18

Это работает для меня в приглашении Unix

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

В приведенной выше команде будут перечислены ФАЙЛЫ, которые не с .svn, и сделанные вами grep.

Ответ 19

Я обычно трассирую вывод через 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'