Как исключить каталог в 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
helion3
источник
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, просто распечатать.

f10bit
источник
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", исключая "Каталог". Сделайте акцент на звезды *.

DimiDak
источник
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
mpapis
источник
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
wisbucky
источник
16

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

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

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

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
user1882879
источник
Я попробовал это, и это все еще спускается в каталоги, таким образом, скорость определенно не улучшена.
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/*"\)

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

JBENOIT
источник
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, не будет проверен на соответствие второй части выражения и не будет напечатан.

Istopopoki
источник
У каждого есть решение, но твое объяснило это лучше всего. Я был непреклонен, чтобы сначала использовалось -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-й ответ и проголосует за него, но можно надеяться. :-)

cycollins
источник
5

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

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

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

find -iname example.com | grep -v beta

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

Siju V
источник
Решение 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/*' \)

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

Funkodebat
источник
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папок.

supersan
источник
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было быстрым и простым решением после некоторой головной боли.

Lemmings19
источник
3

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

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

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

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

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

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

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

jiahut
источник
Лучше ставьте одинарные кавычки вокруг * .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
JaredTS486
источник
Может передать больше указанного каталога.
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
go2null
источник
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с каждым каталогом / результатом из списка

adrianTNT
источник
Подождите секунду, /пока не исключено. Вам может понадобиться 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
EIIPII
источник
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справочной страницы

Alberto
источник
1
Очень старый вопрос, но все еще есть место для улучшений. Я нашел это случайно, пытаясь решить подобную проблему, и ни один из ответов не был удовлетворительным.
Альберто
Я execчасто использую это действие и нахожу его очень полезным. Я обычно добавляю кавычки {}в случае, если в путях к файлам есть пробелы, которые дают "{}".
Людовик Куты
@lkuty Я собирался отредактировать свой пост, чтобы отразить ваш комментарий, но после быстрого теста (без кавычек, {}работает ли для файлов с пробелами в их именах) и просмотра справочных страниц, кажется, что цитирование необходимо только избегать их следует истолковывать как пунктуацию сценария оболочки. В этом случае вы должны использовать одинарные кавычки:'{}'
Альберто
Я думаю, что я должен был использовать это, чтобы сделать cpили mvили rm. Я проверю это
Людовик Куты
1

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

find . \( -name "*" \) -prune -a ! -name "directory"
Веселая Лю
источник