Как исключить каталог в find. команда


1380

Я пытаюсь запустить findкоманду для всех файлов JavaScript, но как исключить конкретный каталог?

Вот findкод, который мы используем.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done

10
Какой каталог вы должны исключить?
— Архетип Павел

11
Это лучше использовать find ... | while read -r file .... Кроме того, лучше принимать и одобрять ответы.
— Приостановлено до дальнейшего уведомления.

в то время как чтение медленно, потому что в это быстрее
— mpapis

18
@mpapis при чтении правильно обрабатывает полные строки с пробелами.
— Жан-Филипп Пелле

1
Просто запустите в папке с файлами с пробелами в именах: for file in $(find .); do echo "$file"; done. Имена с пробелами разделены, чего мы не хотим.
— Жан-Филипп Пелле

Ответы:


1140

Используйте -pruneпереключатель. Например, если вы хотите исключить miscкаталог, просто добавьте -path ./misc -prune -oв команду find:

find . -path ./misc -prune -o -name '*.txt' -print

Вот пример с несколькими каталогами:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print

Здесь мы исключаем dir1 , dir2 и dir3 , так как в findвыражениях это действие, которое действует по критериям -path dir1 -o -path dir2 -o -path dir3(если dir1 или dir2 или dir3 ), а также с type -d.

Дальнейшие действия -o print, просто распечатать.


89
Хм. Это также не работает для меня, так как будет включать игнорируемый каталог "./misc" в выводе.
— Theuni 12.12.12

84
@ Theuni Это, вероятно, не сработало для вас, потому что вы не добавили -print(или любое другое действие) явно после -name. В этом случае обе «стороны» -oпечати заканчиваются, тогда как при использовании -printпечатается только эта сторона.
— Даниэль С. Собрал

4
С man-страницы: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Итак, как мне выполнить удаление с помощью find, если я хочу исключить определенные каталоги из удаления?
— Янис Элмерис

15
Для того, чтобы удалить весь каталог непосредственно от использования результатов: find . -not -path "./.git*". Использование ./dir*вместо ./dir/*удаляет каталог, а также содержимое из вывода.
— micahblu

64
Этот вопрос и путаница в ответах свидетельствуют о том, насколько плохо пользовательский интерфейс find соответствует тому, что нужно людям.
— Йоханнес Оверманн

1932

Если у -pruneвас не работает, это будет:

find -name "*.js" -not -path "./directory/*"

Предостережение: требует прохождения всех нежелательных каталогов.


86
Один из комментариев в принятом ответе указывает на проблему. -pruneне исключает сам каталог, он исключает его содержимое, что означает, что вы получите нежелательную строку в выводе с исключенным каталогом.
— GetFree

96
Отличный ответ. Я бы добавил к этому, что вы можете исключить каталог на ЛЮБОМ уровне, изменив первый .на *. таким образом find -name "*.js" -not -path "*/omitme/*", файлы любого каталога с именем «omitme» будут опущены на любом уровне глубины.
— DeeDee

83
Тем не менее, он по-прежнему пересекает весь нежелательный каталог. Я добавляю свой собственный ответ. :-)
— Даниэль С. Собрал,

18
Обратите внимание, однако, что опция сокращения не работает, только если вы не используете -printявно.
— Даниэль С. Собрал,

39
Было бы лучше сказать «Это альтернатива использованию -prune». Ответы о том, что -prune явно не ошибочны, они просто не такие, как вы.
— Джимбо

459

Я считаю, что следующее легче рассуждать, чем другие предлагаемые решения:

find build -not \( -path build/external -prune \) -name \*.js
# you can also exclude multiple paths
find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

Важное примечание: пути, которые вы вводите после, -pathдолжны точно соответствовать тому, что findбудет напечатано без исключения. Если это предложение Вас смущает только убедитесь , что используются полные пути через из всей команды , как это: . Смотрите примечание [1], если вы хотите лучшего понимания.find /full/path/ -not \( -path /full/path/exclude/this -prune \) ...

Внутри \(и \)это выражение, которое будет точно соответствовать build/external (см. Важное примечание выше), и, в случае успеха, позволит избежать обхода чего-либо ниже . Затем он группируется как одно выражение с экранированными скобками и с префиксом, -notкоторый заставит findпропустить все, что соответствует этому выражению.

Кто-то может спросить, не добавит ли при добавлении -notвсе остальные файлы при -pruneповторном появлении, и ответ - нет. То , как -pruneработает то , что все , что, как только это будет достигнуто, то файлы ниже этого каталога постоянно игнорируются.

Это происходит из реального случая использования, когда мне нужно было вызвать yui-compressor для некоторых файлов, сгенерированных wintersmith, но исключить другие файлы, которые необходимо отправить как есть.


Примечание [1] : Если вы хотите исключить, /tmp/foo/barи вы запускаете поиск, как это " find /tmp \(...", то вы должны указать -path /tmp/foo/bar. Если, с другой стороны, вы запускаете find, как это, cd /tmp; find . \(...вы должны указать -path ./foo/bar.


37
Отличный ответ, спасибо. Это работает и является масштабируемым (читаемым) для нескольких исключений. Вы джентльмены и ученый, сэр. Спасибо за пример для нескольких исключений
— Freedom_Ben

7
Это не работает, если я хочу использовать ключ -delete:find . -not \( -path ./CVS -prune \) -type f -mtime +100 -delete find: The -delete action atomatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
— Jānis Elmeris

17
@Janis Вы можете использовать -exec rm -rf {} \;вместо -delete.
— Даниэль С. Собрал

11
Изучив вывод find, это действительно очевидно, но это сбило меня с толку. Если вы ищете в текущем каталоге (указав в .качестве пути поиска или не указывая один на всех), то вы , скорее всего , хотите , чтобы ваш шаблон после того, как -pathначать с ./, например: find -not \( -path ./.git -prune \) -type f.
— Зантье

7
Более точный (и POSIX-совместимый) вариант этого метода: find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)следуют любые условия, которые должны соответствовать тому, что вы ищете.
— Уолф

219

Здесь явно есть некоторая путаница относительно того, каким должен быть предпочтительный синтаксис для пропуска каталога.

Мнение GNU

To ignore a directory and the files under it, use -prune

Из GNU найти страницу руководства

аргументация

-pruneостанавливается findот спуска в каталог. Простое указание -not -pathбудет все равно попадать в пропущенный каталог, но -not -pathбудет ложным, когда findпроверяется каждый файл.

Проблемы с -prune

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

  1. find печатает сокращенный каталог.

    • TRUE Это намеченное поведение, оно просто не сходит в него. Чтобы вообще не печатать каталог, используйте синтаксис, который логически его опускает.
  2. -pruneработает только с -printи без других действий.

    • НЕ ПРАВДА . -pruneработает с любым действием, кроме -delete. Почему это не работает с удалением? Для -deleteработы find необходимо пройти по каталогу в порядке DFS, так -deleteкак сначала будут удалены листья, затем родители листьев и т. Д. Но для определения -pruneсмысла findнеобходимо обратиться к каталогу и прекратить его нисходящее движение, что явно не имеет смысла с -depthили -deleteна.

Представление

Я установил простой тест из трех самых популярных ответов на этот вопрос (заменил -printна, -exec bash -c 'echo $0' {} \;чтобы показать другой пример действия). Результаты ниже

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

Вывод

Оба синтаксис f10bit в и синтаксис Daniel C. Собрал в взяли 10-25ms бежать в среднем. Синтаксис GetFree , который не используется -prune, занял 865 мс. Итак, да, это довольно экстремальный пример, но если вы заботитесь о времени выполнения и делаете что-то дистанционно интенсивное, вы должны использовать -prune.

Обратите внимание, что синтаксис Даниэля С. Собрала был лучшим из двух -pruneсинтаксисов; но я сильно подозреваю, что это результат некоторого кеширования, так как переключение порядка, в котором выполнялись два процесса, приводило к противоположному результату, в то время как версия без обрезки всегда была самой медленной.

Тестовый скрипт

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup

18
Спасибо за очень хороший анализ. Что касается «Я сильно подозреваю, что это результат некоторого кеширования», вы можете запустить эту команду: sudo sh -c «free && sync && echo 3> / proc / sys / vm / drop_caches && free», чтобы очистить кеш (см. Unix. stackexchange.com/questions/87908/… ).
— ndemou

После нескольких тестов на этих двух -pruneя могу сказать, что редко есть какая-либо разница. Имейте в виду, что какая команда запускается в первую очередь выиграет от производительности процессора, позднее снижение производительности процессора> приведет к незначительному замедлению (я чистил кэш перед каждой командой в соответствии с предложением @ndemou)
— Huy.PhamNhu

Попробуйте переключить число среди name1() name2() name3()в тестовом скрипте @BroSlow выше, чтобы изменить порядок выполнения, чтобы получить визуальное представление о том, что я сказал. В реальной жизни это незаметно между этими двумя, хотя.
— Huy.PhamNhu

Аплодисменты. Спасибо за этот качественный ответ.
— Стефан

Вы не должны быть -о, что означает или. так что вы обрезаете первый шаг, а потом забываете об этом в следующем.
— ммм

97

Это единственный, который работал на меня.

find / -name MyFile ! -path '*/Directory/*'

Поиск "MyFile", исключая "Каталог". Сделайте акцент на звезды *.


13
Этот метод работает в macOS, а принятый ответ - нет. Я знаю, оригинальный вопрос для Linux.
— Ксавье Рубио Янсана

5
Обратите внимание, что вы можете добавить несколько ! -path '*/Directory/*'к вашей команде последовательно, чтобы игнорировать несколько каталогов
— Aclwitt

Работает на MacOS, но не на Linux ... подтверждено
— Marcello de Sales

В docker containerтолько работает сsh -c "find..."
— Марчелло де Сейлс

@ Marcello de Sales Конечно, это работает на Linux.
— ДимиДак

59

Один из вариантов - исключить все результаты, содержащие имя каталога, с помощью grep. Например:

find . -name '*.js' | grep -v excludeddir

44
Это сделает ваш поиск очень медленным
— Дориан

6
Этот работал для меня, другие (которые используют -prune) - нет.
— Андрон

7
Медленно в больших результатах, но полезно в меньших наборах. Но как исключить несколько каталогов, используя grep? Конечно так: find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3но может быть какой-то один способ grep.
— Тимо Кяхконен

6
Если вы хотите , чтобы выполнить несколько отбирает , то вы бы лучше писать его в виде регулярных выражений: egrep -v '(dir1|dir2|dir3)'. Однако в этом конкретном примере лучше было бы исключить каталоги внутри findсебя.
— Лоуренс

1
да, вам не нужны круглые скобки, и было бы лучше использовать ^, чтобы убедиться, что он совпадает с именем каталога в начале строки, например: find. имя * .js | egrep -v "^ \ ./ excludedir1 | ^ \ ./ excludedir2"
— София

41

Я предпочитаю -notобозначения ... это более читабельно:

find . -name '*.js' -and -not -path directory

5
Извините, это не работает. Страница findруководства для говорит: «Чтобы игнорировать каталог и файлы в нем, используйте -prune».
— Кристиан Давен

8
Это не верно. Это не мешает find войти в каталог и просмотреть все файлы внутри.
— GetFree

find . -iname '*' -and -not -path './somePath'не мешает входу в указанный каталог.
— Лемминги19

Это помогло мне с .git path find . -iname '*' -not -path './.git/*'
— Марк Шуст в M.academy

7
@rane: Точнее find . -not -path "*/.git*"было бы то, что вы хотите.
— Бен

20

Используйте опцию -prune. Итак, что-то вроде:

find . -type d -name proc -prune -o -name '*.js'

'-Type d -name proc -prune' ищет только каталоги с именем proc, которые нужно исключить.
'-O' является оператором 'ИЛИ'.


1
Это единственное решение «найти», которое сработало для меня. Каталоги, которые я хотел исключить, НЕ находятся сразу под текущим рабочим каталогом.
— Ламбарт

5
Однако добавление -printв конец может улучшить результаты. find . -type d -name .hg -prune -o -name dataигнорирует содержимое (нескольких) .hgкаталогов, но перечисляет .hgсами каталоги. При этом -printон только перечислил каталоги данных, которые я искал.
— Ламбарт

19

-pruneопределенно работает и является лучшим ответом, потому что он предотвращает спуск в каталог, который вы хотите исключить. -not -pathкоторый все еще ищет исключенный каталог, он просто не печатает результат, что может быть проблемой, если исключенный каталог является подключенным сетевым томом или у вас нет разрешений.

Сложность заключается в том, что findочень важно определить порядок аргументов, поэтому, если вы не получите их правильно, ваша команда может не сработать. Порядок аргументов обычно таков:

find {path} {options} {action}

{path}: Сначала поместите все аргументы, связанные с путем, например . -path './dir1' -prune -o

{options}: У меня больше всего успехов, когда я ставлю -name, -iname, etcпоследний вариант в этой группе. Например-type f -iname '*.js'

{action}: Вы хотите добавить -printпри использовании-prune

Вот рабочий пример:

# setup test
mkdir dir1 dir2 dir3
touch dir1/file.txt; touch dir1/file.js
touch dir2/file.txt; touch dir2/file.js
touch dir3/file.txt; touch dir3/file.js

# search for *.js, exclude dir1
find . -path './dir1' -prune -o -type f -iname '*.js' -print

# search for *.js, exclude dir1 and dir2
find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print

16

Это формат, который я использовал для исключения некоторых путей:

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"

Я использовал это, чтобы найти все файлы не в пути ". *":

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"

Я попробовал это, и это все еще спускается в каталоги, таким образом, скорость определенно не улучшена.
— Br.Bill

10

Подход -path -prune также работает с подстановочными знаками в пути. Вот оператор find, который найдет каталоги для сервера git, обслуживающего несколько репозиториев git, оставив внутренние каталоги git:

find . -type d \
   -not \( -path */objects -prune \) \
   -not \( -path */branches -prune \) \
   -not \( -path */refs -prune \) \
   -not \( -path */logs -prune \) \
   -not \( -path */.git -prune \) \
   -not \( -path */info -prune \) \
   -not \( -path */hooks -prune \)  

9

Чтобы исключить несколько каталогов:

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)

Чтобы добавить каталоги, добавьте -o -path "./dirname/*":

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)

Но, возможно, вам следует использовать регулярное выражение , если нужно исключить много каталогов.


9

Есть много хороших ответов, мне просто потребовалось некоторое время, чтобы понять, для чего предназначен каждый элемент команды и какая логика стоит за ним.

find . -path ./misc -prune -o -name '*.txt' -print

find начнет поиск файлов и каталогов в текущем каталоге, отсюда и find ..

-oВариант обозначает логическое ИЛИ и разделяет две части команды:

[ -path ./misc -prune ] OR [ -name '*.txt' -print ]

Любой каталог или файл, который не является каталогом ./misc, не пройдет первый тест -path ./misc. Но они будут проверены против второго выражения. Если их имя соответствует шаблону, который *.txtони получают, из-за -printопции.

Когда find достигает каталога ./misc, этот каталог удовлетворяет только первому выражению. Так что -pruneопция будет применена к нему. Он сообщает команде find не исследовать этот каталог. Таким образом, любой файл или каталог в ./misc даже не будет проверен командой find, не будет проверен на соответствие второй части выражения и не будет напечатан.


У каждого есть решение, но твое объяснило это лучше всего. Я был непреклонен, чтобы сначала использовалось -name, а не -path. Ваше объяснение было достаточно, чтобы прийти к тому, что я хотел. найти . -name "* .txt" -print -o -path ./misc -prune
— Вендетта V

7

Для рабочего решения (проверено на Ubuntu 12.04 (Precise Pangolin)) ...

find ! -path "dir1" -iname "*.mp3"

будет искать файлы MP3 в текущей папке и подпапках, за исключением подпапки dir1.

Использование:

find ! -path "dir1" ! -path "dir2" -iname "*.mp3"

... чтобы исключить dir1 И dir2


Не работает для меня Ни один из вышеперечисленных ответов. Красная шляпа.
— Тарпа

6

Хорошая уловка для того, чтобы избежать печати сокращенных каталогов - использовать -print(работает -execтакже) после правой стороны -orпосле -prune. Например, ...

find . -path "*/.*" -prune -or -iname "*.j2"

напечатает путь ко всем файлам под текущим каталогом с расширением `.j2", пропуская все скрытые каталоги. Аккуратно. Но он также напечатает на распечатке полный путь каждого каталога, который пропускается, как отмечено выше. Однако, следующее не ...

find . -path "*/.*" -prune -or -iname "*.j2" -print

потому что по логике есть скрытый -andпосле -inameоператора и перед -принтом. Это связывает его с правой частью -orпредложения из-за логического порядка операций и ассоциативности. Но в документах говорится, что есть скрытое, -printесли оно (или любой из его двоюродных братьев ... -print0и т. Д.) Не указано. Так почему же не левая часть -orпечати? Очевидно (и я не понял этого после моего первого прочтения страницы руководства), это верно, если там нет -print-ЛИ -execВСЕГДА, и в этом случае -print логически разбрасывается так, что все печатается. Если даже ОДИНprint операции стиля, выраженной в каком-либо предложении, все эти скрытые логические операции исчезают, и вы получаете только то, что вы уточнить. Честно говоря, я бы предпочел это наоборот, но потомfind только описательных операторов, по-видимому, ничего не сделало бы, так что я думаю, что это имеет смысл, как есть. Как упомянуто выше, все это также работает -exec, поэтому следующее дает полный ls -laсписок для каждого файла с желаемым расширением, но не перечисляет первый уровень каждого скрытого каталога, ...

find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +

Для меня (и других в этом потоке) findсинтаксис довольно быстро становится довольно барочным, поэтому я всегда добавляю парены, чтобы УВЕРЕН, что я знаю, что с чем связано, поэтому я обычно создаю макрос для типа-способности и формирую все такие операторы как. ..

find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)

Трудно ошибиться, настроив мир на две части. Я надеюсь, что это поможет, хотя вряд ли кто-то зачитает 30-й ответ и проголосует за него, но можно надеяться. :-)


5

Вы можете использовать опцию чернослива для достижения этой цели. Как например:

find ./ -path ./beta/* -prune -o -iname example.com -print

Или обратный вариант grep «grep -v»:

find -iname example.com | grep -v beta

Вы можете найти подробные инструкции и примеры в Linux. Команда find исключает каталоги из поиска .


Решение grep - единственное, которое исключает все каталоги с одинаковыми именами. При попытке исключить "node_modules", что весьма полезно.
— bmacnaughton

3
@bmacnaughton - не правда! Я пришел сюда специально, чтобы исключить "node_modules", и после прочтения многих прекрасных ответов я остановился на find . -type f -print -o -path "*/node_modules" -prune... с использованием подстановочного знака, который пропускает "node_modules" на любом уровне; использование -printпервого варианта -type f -printприводит к печати только этой части, поэтому сами каталоги "node_modules" не перечислены. (он также может быть отменено: find . -path "*/node_modules" -prune -o -type f -print)
— Стивен P

что * / делает там. Какой именно файл вы хотите исключить. Используете ли вы его в качестве подстановочного знака?
— Siju V

1
@StephenP, спасибо за указание на это; Я узнал разницу между использованием ./node_modulesи */node_modulesот него. Для моего случая, когда node_modulesсуществует только в каталоге, в котором я начинаю поиск (и в этом node_modulesкаталоге), я могу использовать, find . -type f -print -o -path "./node_modules" -prune потому что не будет node_modulesкаталога в любом другом каталоге.
— bmacnaughton

1
@SijuV - в каталоге, где я искал, был node_modulesподкаталог, но были также подкаталоги, которые имели свои собственные node_modules ... используя ./node_modulesсовпадения только с подкаталогом node_modulesв текущем каталоге .и удаляя его; используя */node_modulesспички и чернослив каталог на любой глубине, так *как Глоб соответствует любому ведущему префиксу пути, например ./test5/main/node_modules, не только ./приставка. Это *подстановочный знак, но не как регулярное выражение.
— Стивен П.

5
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune

Не могу заставить это работать. find ~/Projects -name '*.js' -\! -name 'node_modules' -pruneдо сих пор node_modules
— обнаруживает

1
@mpen, из stackoverflow.com/questions/4210042/… я узнал, что синтаксис, который вы хотите, это find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Имя этого пути должно точно соответствовать тому, что находил бы вывод find, если собирался распечатать каталог.
— PatS

4
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'

кажется, работает так же, как

find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)

и легче запомнить ИМО.


4

TLDR: понять ваши корневые каталоги и настроить поиск оттуда, используя -path <excluded_path> -prune -oопцию. Не включать трейлинг/ в конце исключенного пути.

Пример:

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print


findЯ считаю, что для эффективного использования крайне важно хорошо понимать структуру каталогов вашей файловой системы. На моем домашнем компьютере установлены жесткие диски с несколькими ТБ, резервная копия которых хранится примерно в половине rsnapshot( с помощью rsync). Несмотря на то, что резервное копирование выполняется на физически независимый (дублированный) диск, он монтируется в моей системной /директории root ( ) /mnt/Backups/rsnapshot_backups/:

/mnt/Backups/
└── rsnapshot_backups/
    ├── hourly.0/
    ├── hourly.1/
    ├── ...
    ├── daily.0/
    ├── daily.1/
    ├── ...
    ├── weekly.0/
    ├── weekly.1/
    ├── ...
    ├── monthly.0/
    ├── monthly.1/
    └── ...

/mnt/Backups/rsnapshot_backups/Каталог в настоящее время занимает ~ 2,9 ТБ, с ~ 60м файлов и папок; простой просмотр этого содержимого требует времени:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find /mnt/Backups/rsnapshot_backups | wc -l
60314138    ## 60.3M files, folders
34:07.30    ## 34 min

time du /mnt/Backups/rsnapshot_backups -d 0
3112240160  /mnt/Backups/rsnapshot_backups    ## 3.1 TB
33:51.88    ## 34 min

time rsnapshot du    ## << more accurate re: rsnapshot footprint
2.9T    /mnt/Backups/rsnapshot_backups/hourly.0/
4.1G    /mnt/Backups/rsnapshot_backups/hourly.1/
...
4.7G    /mnt/Backups/rsnapshot_backups/weekly.3/
2.9T    total    ## 2.9 TB, per sudo rsnapshot du (more accurate)
2:34:54          ## 2 hr 35 min

Таким образом, в любое время мне нужно искать файл на моем / (корневом) разделе, я должен иметь дело с (избегать, если возможно) обходом моего раздела резервных копий.


ПРИМЕРЫ

Среди различных подходов, предложенных в этой теме ( Как исключить каталог из команды find. ), Я обнаружил, что поиск с использованием принятого ответа выполняется намного быстрее - с оговорками.

Решение 1

Допустим, я хочу найти системный файл libname-server-2.a, но не хочу искать в своих rsnapshotрезервных копиях. Чтобы быстро найти системный файл, используйте путь исключения /mnt(т. Е. Используйте /mnt, не /mnt/, или /mnt/Backups, или ...):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a
real    0m8.644s              ## 8.6 sec  <<< NOTE!
user    0m1.669s
 sys    0m2.466s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 3 sec     ## ~3 sec  <<< NOTE!

... находит этот файл всего за несколько секунд, в то время как это занимает гораздо больше времени (кажется, что выполняется через все «исключенные» каталоги):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt/ -prune -o -name "*libname-server-2.a*" -print
find: warning: -path /mnt/ will not match anything because it ends with /.
/usr/lib/libname-server-2.a
real    33m10.658s            ## 33 min 11 sec (~231-663x slower!)
user    1m43.142s
 sys    2m22.666s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt/ -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 1775 sec    ## 29.6 min

Решение 2

Другое решение, предлагаемое в этой теме ( SO # 4210042 ), также работает плохо:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -name "*libname-server-2.a*" -not -path "/mnt"
/usr/lib/libname-server-2.a
real    33m37.911s            ## 33 min 38 sec (~235x slower)
user    1m45.134s
 sys    2m31.846s

time find / -name "*libname-server-2.a*" -not -path "/mnt/*"
/usr/lib/libname-server-2.a
real    33m11.208s            ## 33 min 11 sec
user    1m22.185s
 sys    2m29.962s

РЕЗЮМЕ | ВЫВОДЫ

Используйте подход, показанный в « Решении 1 »

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print

т.е.

... -path <excluded_path> -prune -o ...

отмечая, что всякий раз, когда вы добавляете конечный /путь к исключенному пути, findкоманда затем рекурсивно вводит (все эти) /mnt/*каталоги - что в моем случае из-за /mnt/Backups/rsnapshot_backups/*подкаталогов дополнительно включает в себя ~ 2,9 ТБ файлов для поиска! Не добавляя трейлинг, /поиск должен завершиться практически сразу (в течение нескольких секунд).

«Решение 2» ( ... -not -path <exclude path> ...) также, по-видимому, рекурсивно ищет в исключенных каталогах - не возвращает исключенные совпадения, но излишне отнимает это время поиска.


Поиск в этих rsnapshotрезервных копиях:

Чтобы найти файл в одной из моих ежечасных / ежедневных / еженедельных / ежемесячных rsnapshotрезервных копий):

$ START="$(date +"%s")" && find 2>/dev/null /mnt/Backups/rsnapshot_backups/daily.0 -name '*04t8ugijrlkj.jpg'; END="$(date +"%s")"; TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg
find command took 312 sec   ## 5.2 minutes: despite apparent rsnapshot size
                            ## (~4 GB), it is in fact searching through ~2.9 TB)

Исключая вложенный каталог:

Здесь я хочу исключить вложенный каталог, например, /mnt/Vancouver/projects/ie/claws/data/*при поиске из /mnt/Vancouver/projects/:

$ time find . -iname '*test_file*'
./ie/claws/data/test_file
./ie/claws/test_file
0:01.97

$ time find . -path '*/data' -prune -o -iname '*test_file*' -print
./ie/claws/test_file
0:00.07

В сторону: Добавление -printв конце команды подавляет распечатку исключенного каталога:

$ find / -path /mnt -prune -o -name "*libname-server-2.a*"
/mnt
/usr/lib/libname-server-2.a

$ find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a

Это не размер файлов, которые замедляются find, это количество записей каталога, которые он должен проверить. Поэтому гораздо хуже, если у вас есть много-много маленьких файлов (особенно, если они все связаны по нескольким ссылкам!), Чем если у вас просто несколько файлов размером в несколько гигабайт.
— Тоби Спейт

@TobySpeight: хорошая мысль. Я упомянул размер пространства поиска, чтобы указать масштаб, который также содержит много файлов. Быстрый поиск в корне (/) с sudo ls -R / | wc -lуказанием ~ 76,5 млн файлов (большая часть которых резервируется, за исключением системных файлов «неконфигурированных»); /mnt/Vancouver/с ls -R | wc -lуказывает ~ 2,35 млн файлов; /home/victoria/содержит 0.668M файлов.
— Виктория Стюарт

4

Вы также можете использовать регулярные выражения для включения / исключения некоторых файлов / каталогов при поиске, используя что-то вроде этого:

find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*" 

Это только даст вам все JS, Vue, CSS, и т.д. файлы , но исключая все файлы в node_modulesи vendorпапок.


3

Я использовал findдля предоставления списка файлов xgettextи хотел опустить конкретный каталог и его содержимое. Я перепробовал множество -pathкомбинаций с, -pruneно не смог полностью исключить каталог, который хотел удалить.

Хотя мне удалось игнорировать содержимое каталога, который я хотел игнорировать, я findвернул сам каталог как один из результатов, что вызвалоxgettext к сбою в результате (не принимает каталоги; только файлы).

Мое решение было просто использовать, grep -vчтобы пропустить каталог, который я не хотел в результатах:

find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext

Есть ли аргумент для findэтого будет работать на 100%, я не могу сказать наверняка. Использование grepбыло быстрым и простым решением после некоторой головной боли.


3

Ни один из предыдущих ответов не подходит для Ubuntu. Попробуй это:

find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"

Я нашел это здесь


Я не вижу причин, по которым любой из ответов с более чем 100 баллами не должен работать на Ubuntu.
— Аксель Бекерт

ммм посмотрим? может потому что я их всех перепробовал?
— Sixro

find везде одинаковая реализация во всех дистрибутивах Linux - одна из проекта GNU. Единственная разница может быть в версиях. Но изменения за последнее десятилетие не были такими уж агрессивными, разве что для согласования разрешений.
— Аксель Бекерт

3

Это подходит для меня на Mac:

find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune

Это исключит vendorи app/cachedir для поиска имени, к которому добавлен суффикс php.


Лучше ставьте одинарные кавычки вокруг * .php, иначе вы не найдете то, что ищете.
— Br.Bill

3

Для тех из вас, кто работает в старых версиях UNIX и не может использовать -path или -not

Протестировано на SunOS 5.10 bash 3.2 и SunOS 5.11 bash 4.4

find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f

Может передать больше указанного каталога.
— MUY Бельгия

2

Лоуренс Гонсалвес (Laurence Gonsalves ) - отличный ответ о том, как -pruneработает, как использовать prune-option-find-in-sh .

И вот общее решение:

find /path/to/search                    \
  -type d                               \
    \( -path /path/to/search/exclude_me \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print

Чтобы не печатать /path/to/seach/несколько раз, заверните findв pushd .. popdпару.

pushd /path/to/search;                  \
find .                                  \
  -type d                               \
    \( -path ./exclude_me               \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print;         \
 popd

1
Из stackoverflow.com/questions/4210042/… я узнал, что синтаксис, используемый для -pathобязательного имени, должен совпадать с именем, которое находит печать, если бы, например, распечатать каталог find . -path ./.git -prune -o -print, или find $HOME/foo -path $HOME/foo/.git -prune -o -print некоторые из ответов просто говорят, -path somedirчто, к сожалению, не достаточно точен, чтобы быть полезным.
— PatS

2

Для того, что мне было нужно, это работало так, находя landscape.jpgна всех серверах, начиная с root, и исключая поиск по /varкаталогу:

find / -maxdepth 1 -type d | grep -v /var | xargs -I '{}' find '{}' -name landscape.jpg

find / -maxdepth 1 -type dсписок всех d irectories в/

grep -v /var исключает `/ var 'из списка

xargs -I '{}' find '{}' -name landscape.jpgвыполнить любую команду, как findс каждым каталогом / результатом из списка


Подождите секунду, /пока не исключено. Вам может понадобиться sed 1d.
— Симба

2

Следующие команды работают:

find . -path ./.git -prune -o -print

Если у вас возникли проблемы с поиском, используйте -D treeопцию для просмотра информации анализа выражения.

find -D tree . -path ./.git -prune -o -print

Или -D all, чтобы увидеть всю информацию о выполнении.

find -D all . -path ./.git -prune -o -print

1

Я нашел имя функции в исходных файлах C исключая * .o и исключая * .swp и exclude (не обычный файл) и исключая вывод dir с помощью этой команды:

find .  \( ! -path "./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach

1

Лучше использовать execдействие, чем forцикл:

find . -path "./dirtoexclude" -prune \
    -o -exec java -jar config/yuicompressor-2.4.2.jar --type js '{}' -o '{}' \;

exec ... '{}' ... '{}' \;Будет выполняться один раз для каждого файла сопоставления, заменив скобки '{}'с текущим именем файла.

Обратите внимание, что фигурные скобки заключены в одинарные кавычки, чтобы защитить их от интерпретации как пунктуации сценария оболочки * .


Ноты

* Из раздела «Примеры» find (GNU findutils) 4.4.2справочной страницы


1
Очень старый вопрос, но все еще есть место для улучшений. Я нашел это случайно, пытаясь решить подобную проблему, и ни один из ответов не был удовлетворительным.
— Альберто

Я execчасто использую это действие и нахожу его очень полезным. Я обычно добавляю кавычки {}в случае, если в путях к файлам есть пробелы, которые дают "{}".
— Людовик Куты

@lkuty Я собирался отредактировать свой пост, чтобы отразить ваш комментарий, но после быстрого теста (без кавычек, {}работает ли для файлов с пробелами в их именах) и просмотра справочных страниц, кажется, что цитирование необходимо только избегать их следует истолковывать как пунктуацию сценария оболочки. В этом случае вы должны использовать одинарные кавычки:'{}'
— Альберто

Я думаю, что я должен был использовать это, чтобы сделать cpили mvили rm. Я проверю это
— Людовик Куты

1

Я попробовал команду выше, но ни один из тех, кто использует "-prune", не работает для меня. В конце концов я попробовал это с командой ниже:

find . \( -name "*" \) -prune -a ! -name "directory"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.