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

216

Я часто использую команду 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 для метаданных для этой рабочей копии.

  • 3
    Для производительности попробуйте использовать find ... -print0 | xargs -0 egrep ... вместо find ... -exec grep ... (не распаковывает grep для каждого файла, но для нескольких файлов одновременно). Используя эту форму, вы также можете .svn каталоги .svn не используя опцию -prune для find, т.е. find ... -print0 | egrep -v '/\.svn' | xargs -0 egrep ...
  • 2
    @Vlad: Насколько я знаю, использование -exec с + не приводит к форку grep для каждого файла при использовании его с ; делает. Использование -exec на самом деле более правильно, чем использование xargs . Обратите внимание, что такие команды, как ls что-то делают, даже если список аргументов пуст, в то время как такие команды, как chmod выдают ошибку, если аргументов недостаточно. Чтобы понять, что я имею в виду, просто попробуйте следующую команду в каталоге, где нет сценария оболочки: find /path/to/dir -name '*.sh' -print0 | xargs -0 chmod 755 . Сравните с этим: find /path/to/dir -name '*.sh' -exec chmod 755 '{}' '+' .
Показать ещё 4 комментария
Теги:
find
grep
svn

19 ответов

68
Лучший ответ

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

  • 3
    Мне очень нравится ack , но я обнаружил, что он значительно медленнее, чем find -type f -name "*.[ch]" | xargs grep при работе с большой кодовой базой.
  • 1
    Попробуйте findrepo для скорости. pixelbeat.org/scripts/findrepo
Показать ещё 7 комментариев
273

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

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

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

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

find -not -iwholename '*.svn' -name 'messages.*' -exec grep -Iw uint {} + \;
  • 4
    Супер большой +1 для "-not" и "-iwholename". Ack - это замечательно, и я им пользуюсь, но find / exec все еще находит применение.
  • 9
    Единственный ответ, который фактически ответил на оригинальный вопрос.
Показать ещё 5 комментариев
135

Как следует:

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

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

find . -name .svn -a -type d -prune -o -print
  • 13
    @Kaleb: Привет. Я предлагаю find . -type d -name .svn -prune -o -print потому что это немного быстрее. Согласно стандарту POSIX , выражения оцениваются одно за другим в указанном порядке. Если первое выражение в -a имеет значение false , второе выражение не будет оцениваться (также называемое коротким замыканием и оценкой ).
  • 2
    @Kaleb: поскольку сравнение типа файла (эквивалентно проверке того, установлен ли бит в целом числе) быстрее, чем сравнение имени файла (эквивалентно сравнению строки, то есть O (n)), ставя -type d перед -name .svn теоретически более эффективен. Тем не менее, это обычно незначительно, за исключением случаев, когда у вас очень очень большое дерево каталогов.
Показать ещё 4 комментария
26

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

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

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

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

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

  • 0
    Лучший ответ IMO. Другие пытаются объяснить вещи, которые не отвечают на простой вопрос.
17

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

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 для ваших конкретных проблем.

12

Найти GNU

find .  ! -regex ".*[/]\.svn[/]?.*"
  • 0
    Я загружал пути к каталогам в массив для обработки PHP. Другие ответы выше (по какой-либо причине) не отфильтровали файлы в поиске (несмотря на -type d ) - этот ответ сделал. +1
9

Я использую 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 включают его, но, если я правильно помню, мне пришлось скомпилировать свой собственный grep (или попросить сделать это у homebrew) в OSX.
  • 0
    Я использую второстепенный вариант этого. Мой .bashrc создает функцию Bash 'grp', которая определяется как GREP_OPTIONS=xxx grep "$@" . Это означает, что переменная GREP_OPTIONS установлена только для экземпляров grep, которые я запускаю вручную, используя 'grp'. Это означает, что у меня никогда не возникает ситуация, когда я запускаю инструмент, и внутренне он вызывает grep, но инструмент запутывается, потому что grep ведет себя не так, как ожидалось. Кроме того, у меня есть вторая функция grpy, которая вызывает grp, но добавляет --include=*.py , просто для поиска файлов Python.
Показать ещё 1 комментарий
8

find . | grep -v \.svn

  • 0
    Ты должен сбежать . в регулярном выражении .svn .
  • 4
    Используйте --fixed-strings с grep: | fgrep -v /.svn/ или `| grep -F -v / .svn / `для исключения именно каталога, а не файлов с" .svn "как частью их имени.
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;
  • 0
    Этот скрипт не работает, как я ожидал. При запуске с помощью «svnfind -type f» он также печатает svn-каталоги и файлы в svn-каталогах
  • 0
    @ifischer Можете ли вы добавить echo к команде поиска и сказать, какая команда выполняется? svnfind -type f отлично работает на моей машине Red Hat.
Показать ещё 7 комментариев
7

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

your find command| grep -v '\.svn'
  • 0
    Ты должен сбежать . в регулярном выражении .svn .
  • 0
    @Vlad Ты уверен?
Показать ещё 3 комментария
5

Просто подумал, что добавлю простую альтернативу к сообщениям 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 недоступен.)

  • 1
    @Vlad: Обратите внимание, что для параметра -exec в find есть две формы: одна заканчивается на ; а другой заканчивается + . Буква, оканчивающаяся на + заменяет {} списком всех подходящих файлов. Кроме того, ваше регулярное выражение '/\.svn' совпадает с именами файлов, такими как '.svn.txt' . Пожалуйста, обратитесь к моим комментариям к вопросу для получения дополнительной информации.
  • 2
    @Vlad: вот стандарт POSIX для утилиты find . Пожалуйста, смотрите часть -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 не в пути к целевому каталогу.

2

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

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

2

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

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

2

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

2

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

findrepo uint 'messages.*'
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'
1

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

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

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

  • 0
    «gfind» - опечатка? У меня его нет в Ubuntu 14.04.
  • 0
    Предполагая, что вы имели в виду «найти», это не совсем работает. Он также отфильтровывает файлы, такие как xxx.svnxxx . Это важно - например, если вы используете git вместо svn, вам часто нужно включать файлы, такие как .gitignore (это не метаданные, это обычный файл, который включен в репозиторий) в результаты поиска.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню