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

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

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

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

46 ответов

Решение

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

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), ANDed с type -d, Дальнейшие действия -o printПросто распечатай.

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

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

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

find build -not \( -path build/external -prune \) -name \*.js

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

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

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

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

find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

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

Мнение 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 для работы нужно найти каталог в порядке 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. Sobral для выполнения работали в среднем 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

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

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

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

Обратите внимание, что ./ раньше и /* после имени папки необходимо исключить, чтобы исключить, и все, что в ней! Т.е. это работает:

      # This WORKS!
find -not -path "./dir_to_exclude/*"

... но они НЕ работают:

      # These do NOT work!
find -not -path "dir_to_exclude"
find -not -path "dir_to_exclude/*"
find -not -path "./dir_to_exclude"
find -not -path "./dir_to_exclude/"

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

Оттуда я люблю трубить grepдля поиска определенных совпадающих шаблонов на интересующих путях. Пример: поиск любого пути, который НЕ находится внутри dir_to_exclude каталог, и который имеет desired_file_name.txt в этом:

      # Case-sensitive; notice I use `\.` instead of `.` when grepping, in order to
# search for the literal period (`.`) instead of the regular expression
# wildcard char, which is also a period (`.`).
find -not -path "./dir_to_exclude/*" | grep "desired_file_name\.txt"

# Case-INsensitive (use `-i` with your `grep` search)
find -not -path "./dir_to_exclude/*" | grep -i "desired_file_name\.txt"

# To make `dir_to_exclude` also case INsensitive, use the `find` `-ipath` option
# instead of `-path`:
find -not -ipath "./dir_to_exclude/*" | grep -i "desired_file_name\.txt"

Использованная литература:

  1. [основной ответ на этот вопрос] Как исключить каталог из find. команда
  2. https://unix.stackexchange.com/questions/350085/is-it-possible-to-exclude-a-directory-from-the-find-command/350172#350172
  3. https://unix.stackexchange.com/questions/32155/find-command-how-to-ignore-case/32158#32158

Смотрите также:

  1. [Мне все еще нужно изучить и прочитать это] https://www.baeldung.com/linux/find-exclude-paths

Ключевые слова: исключить каталог из команды поиска; не ищите путь с помощью find; нечувствительные к регистру команды find и grep

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

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

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

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

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

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

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

-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

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

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

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

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

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

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

Подход -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 \)  

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

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

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

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

потому что логически есть скрытый -and после -inameоператор и перед -print. Это привязывает его к правой части-orпредложение из-за логического порядка операций и ассоциативности. Но в документах говорится, что есть скрытый-print если он (или любой из его кузенов... -print0и т.д.) не указано. Так почему же не левая часть-orпечать? По-видимому (и я не понял этого с первого чтения страницы руководства), это правда, если там нет-print -или -execВЕЗДЕ, и в этом случае -print логически разбросан так, что все будет напечатано. Если хотя бы ОДИНprintОперация -style выражается в любом предложении, все скрытые логические элементы удаляются, и вы получаете только то, что указали. Откровенно говоря, я бы предпочел наоборот, но тогда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-го ответа и проголосует за него, но можно надеяться.:-)

Если вы ищете высокопроизводительный ответ, то это:

      find . -type d -name node_modules -prune -false -o -type f

Использовать -falseчтобы исключить сам node_modules.

Это будет в 3 раза быстрее, чем -not -pathподход в каталоге с 10000 файлами в node_modules.

      find . -type f -not -path '*node_modules*'

И если в node_modules больше файлов, вы получите гораздо более высокую производительность.

Если кто-то изучает, как игнорировать сразу несколько путей. Вы можете использовать массивы bash (отлично работает с GNU bash, версия 4.4.20(1)-release)

      #!/usr/bin/env bash

# This script helps ignore unnecessary dir paths while using the find command

EXCLUDE_DIRS=(
    "! -path /*.git/*"
    "! -path /*go/*"
    "! -path /*.bundle/*"
    "! -path /*.cache/*"
    "! -path /*.local/*"
    "! -path /*.themes/*"
    "! -path /*.config/*"
    "! -path /*.codeintel/*"
    "! -path /*python2.7/*"
    "! -path /*python3.6/*"
    "! -path /*__pycache__/*"
)
find $HOME -type f ${EXCLUDE_DIRS[@]}

# if you like fzf

find $HOME -type f ${EXCLUDE_DIRS[@]} | fzf --height 40% --reverse

Также по какой-то причине вы не сможете игнорировать пути к каталогам / bin /.

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

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

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

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

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

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

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

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

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

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

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

find . \( -path '.**/.git' -o -path '.**/.hg' \) -prune -o -name '*.js' -print

В приведенном выше примере найдены все *.js файлы в текущем каталоге, исключая папки .git а также .hg, не имеет значения, насколько глубоко эти .git а также .hg папки есть.

Примечание: это также работает:

find . \( -path '.*/.git' -o -path '.*/.hg' \) -prune -o -name '*.js' -print

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

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

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

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

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

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

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

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

Это даст вам только все файлы js, vue, css и т.д., но исключая все файлы в node_modules а также vendor папки.

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

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

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

find -iname example.com | grep -v beta

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

Здесь уже есть много ответов; Я не хочу добавлять еще один, но я думаю, что эта информация полезна.

TLDR: понять ваши корневые каталоги и настроить поиск оттуда, используя " -prune вариант.

Фон: у меня есть rsnapshot (rsync) резервное хранилище, /mnt/Backups/, что вызывает головную боль при поиске системы (/), поскольку эти резервные копии содержат ~ 4,5 ТБ (терра) файлов!

у меня тоже есть /mnt/Vancouver, моя основная рабочая папка с ТБ файлов, которая резервируется [ /mnt/Backups/ а также /mnt/Vancouver/ физически (избыточно) монтируются на отдельных дисках].


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

Этот

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

находит этот файл за ~3-4 секунды; этот

find / -name "*libname-server-2.a*" -not -path "/mnt/*"

появляется (?) для рекурсии через все исключенные каталоги (глубоко вложенные rsync снимки всех смонтированных томов), так что это займет вечность. Я предполагаю, что он ищет файлы по нескольким ТБ, так что он застрял бесконечно. Например, если я попытаюсь "время", что поиск (time find ...), Я вижу обильный вывод - предполагая, что find глубоко перебирает "исключенный" каталог:

...
find: ‘/mnt/Backups/rsnapshot_backups/monthly.0/snapshot_root/var/lib/udisks2’: Permission denied
...

Добавление косой черты после исключенного каталога (/mnt/) или вложенный путь (`/mnt/Backups') приводит к тому, что этот поиск снова * принимает навсегда:

Медленный:

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

"РЕШЕНИЕ"

Вот лучшие решения (все они выполняются в считанные секунды). Опять же, моя структура каталогов

  • / root
  • /mnt/Backups/: резервные копии на несколько туберкулезов
  • /mnt/Vancouver/: многотуберкулезный рабочий каталог (резервная копия на /mnt/Backups на отдельном диске), который я часто хочу искать
  • /home/*: другие точки монтирования / рабочие "диски" (например, /home/victoria знак равно ~)

Системные файлы ( / ):

Чтобы быстро найти системный файл, исключите /mnt (не /mnt/ или же /mnt/Backups, ...):

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

который находит этот файл за ~3-4 секунды.

Несистемные файлы:

Например, чтобы быстро найти файл на одном из моих двух работающих "дисков", /mnt/Vancouver/ и / или /home/victoria/).

$ find /mnt/Vancouver/ -name "*04t8ugijrlkj.jpg"
/mnt/Vancouver/temp/04t8ugijrlkj.jpg

$ find /home/victoria -iname "*Untitled Document 1"
/home/victoria/backups/shortcuts.bak.2016.11.02/Untitled Document 1
/home/victoria/Untitled Document 1

Резервные копии:

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

$ find /mnt/Backups/rsnapshot_backups/daily.0 -name "*04t8ugijrlkj.jpg"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg 

В сторону: Добавление -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 . -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

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

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

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

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

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

Это исключит vendor а также app/cache dir для поиска имени с суффиксом php,

Я использовал 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 было быстрое и простое решение после некоторой головной боли.

Для тех из вас, кто работает в старых версиях 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

Вы также можете использовать

      find  -type f -not -name .directoryname -printf "%f\n"
Другие вопросы по тегам