Як виключити каталог у пошуку. командування


1379

Я намагаюся запустити 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 ), з операції AND type -d.

Подальша дія - -o printпросто надрукувати.


89
Хм. Це не працює для мене, оскільки він буде включати в ігнорований каталог "./misc".
Теуні

84
@Theuni Це, ймовірно, не працювало для вас, тому що ви не додали -print(або будь-яку іншу дію) явно після -name. У цьому випадку обидві "сторони" -oзакінчуються друком, тоді як якщо ви використовуєте -print, друкується лише ця сторона.
Даніель К. Собрал

4
З manpage: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Отже, як я можу перейти до видалення, якщо я хочу виключити певні каталоги зі видалення?
Jānis Elmeris

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

64
Це запитання і плутанина у відповідях є проявом того, наскільки погано користувальницький інтерфейс відповідає тому, що потрібно людям.
Йоганнес Оверманн

1931

Якщо -pruneдля вас не працює, це:

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

Caveat: вимагає пройти всі небажані каталоги.


86
Один із коментарів у прийнятій відповіді вказує на проблему. -pruneне виключає сам каталог, він виключає його вміст, а це означає, що ви отримаєте небажаний рядок у висновку з виключеним каталогом.
GetFree

95
Чудова відповідь. Я додам до цього, що ви можете виключити каталог на будь-якому рівні, змінивши перший .на *. таким чином, ви find -name "*.js" -not -path "*/omitme/*"б опускали файли з каталогу під назвою "omitme" на будь-якому рівні глибини.
DeeDee

83
Він все ще обходить весь небажаний каталог. Я додаю власну відповідь. :-)
Даніель К. Собрал

18
Однак зауважте, що опція чорносливу не працює, лише якщо ви не використовуєте -printявно.
Даніель К. Собрал

39
Було б краще сказати «Це альтернатива використанню -prune». Відповіді, які напрошують -прун, явно не помиляються, вони просто не такі, як ви це зробили б.
Джимбо

458

Мені здається, що простіше міркувати про інші запропоновані рішення:

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було б надруковано без виключення. Якщо це речення збиває з пантелику, ви просто переконайтеся, що ви використовуєте цілі шляхи через цілу команду:find /full/path/ -not \( -path /full/path/exclude/this -prune \) ... . Дивіться примітку [1], якщо ви хочете краще зрозуміти.

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

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

Це відбувається з фактичного випадку використання, коли мені потрібно було викликати yui-компресор на деяких файлах, створених wintersmith, але залишати інші файли, які потрібно надіслати як є.


Примітка [1] : Якщо ви хочете виключити, /tmp/foo/barі ви запускаєте пошук такого типу, то " find /tmp \(..." потрібно вказати -path /tmp/foo/bar. Якщо з іншого боку ви запускаєте такий варіант, 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 \)супроводжуються будь-якими умовами, які повинні відповідати тому, що ви шукаєте.
Вальф

217

Тут явно є деяка плутанина щодо того, яким повинен бути переважний синтаксис для пропуску каталогу.

Думка GNU

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

На сторінці GNU знайти людину

Обґрунтування

-pruneзупиняється findвід спуску в каталог. Щойно вказівка -not -pathвсе ще зійде в пропущений каталог, але -not -pathбуде хибною щоразуfind тестується кожен файл.

Випуски с -prune

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

  1. find друкує обрізаний каталог.

    • ІСТИНА Це призначена поведінка, вона просто не спускається в неї. Щоб взагалі не надрукувати каталог, використовуйте синтаксис, який логічно опускає його.
  2. -pruneпрацює лише з -printіншими діями та без жодних інших дій.

    • НЕ ІСТИНА . -pruneпрацює з будь-якою дією, крім -delete. Чому це не працює з видаленням? Для -deleteроботи знайдіть необхідність перейти до каталогу в порядку DFS, оскільки -deleteспочатку видалять листя, потім батьки залишають і т. Д. Але для уточнення, -pruneщоб мати сенс, findпотрібно натиснути на каталог і припинити його зменшувати, що явно не має сенсу з -depthчи -deleteввімкненням.

Продуктивність

Я створив простий тест з трьох головних upvoted відповідей на це питання (замінений -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

Оплески. Дякую за відповідь на якість.
Сфан

Ви не повинні бути -о, що означає або. тож ви підрізаєте на першому кроці, а потім забудете про все на наступному.
ммм

95

Це єдиний, хто працював на мене.

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.
DimiDak

59

Одним із варіантів було б виключити всі результати, які містять ім'я каталогу із грепом. Наприклад:

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

44
Це зробить ваш пошук дуже повільним
Доріан

6
Цей працював на мене, інші (які використовують -prune) - ні.
Андрон

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

6
Якщо ви хочете , щоб виконати кілька відбирає , то ви б краще писати його в вигляді регулярних виразів: egrep -v '(dir1|dir2|dir3)'. Однак у цьому конкретному випадку було б краще виключити каталоги всередині findсебе.
Лоранс

1
так, і вам не потрібні дужки, і було б краще використовувати ^ для забезпечення відповідності імені каталогу на початку рядка, наприклад: find. -імен '* .js' | egrep -v "^ \ ./ iskljuнdir1 | ^ \ ./ iskljuнdir2"
Софія

41

Я віддаю перевагу -notпозначенням ... це читабельніше:

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

5
Вибачте, це не працює. Сторінка findдовідки говорить: "Щоб ігнорувати каталог та файли під ним, використовуйте -prune".
Крістіан Давен

8
Це неправильно. Це не заважає знайти вхід у каталог і пройти всі файли всередині.
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'

'-Тип 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який все ще шукає виключений dir, він просто не друкує результат, що може бути проблемою, якщо виключений dir встановлений мережевим томом або ви не маєте дозволів.

Хитра частина полягає в тому, що 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

Підхід -prune-prune також працює з подвійними кодами на шляху. Ось констатація пошуку, яка знайде каталоги для 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 опцію.

Коли пошук доходить до каталогу ./misc, цей каталог задовольняє лише першому виразу. Тож -pruneваріант буде застосований до нього. Він повідомляє команду find не досліджувати цей каталог. Таким чином, будь-який файл або каталог у ./misc навіть не буде досліджено шляхом пошуку, не буде перевірено на другу частину виразу і не буде надруковано.


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

7

Для робочого рішення (тестується на Ubuntu 12.04 (Precision Pangolin)) ...

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

буде шукати MP3-файли у поточній папці та вкладених папках, за винятком підпапки dir1.

Використання:

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

... виключити dir1 AND 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друку? Мабуть (і я цього не зрозумів з мого першого читання сторінки man), це правда, якщо немає такої, яка має лише описові оператори, мабуть, нічого б не зробили, тому я думаю, це має сенс, як це є. Як було сказано вище, це все також працює , тому нижче наведено повний перелік кожного файлу з потрібним розширенням, але не перелік першого рівня кожного прихованого каталогу, ...-print -ЩО -execВСІМ, в такому випадку -друк логічно посипається навколо, щоб все друкувалося. Якби навіть ОДИНprint операції -style, виражається в будь-якому пункті, всі ці приховані логічні відходять і ви отримуєте лише те, що ви вкажіть. Відверто кажучи, я, можливо, вважав за краще це навпаки, але тодіfind-execls -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 -v":

find -iname example.com | grep -v beta

Ви можете знайти докладні вказівки та приклади в команді пошуку Linux, щоб виключити каталоги з пошуку .


Рішення 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

що там * / роблять там. Який точний файл ви хочете виключити. Чи використовуєте його в якості підстановки?
Сіджу 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, не тільки ./префіксу. The *wildcard, але як глобус не як регулярний вираз.
Stephen P

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

Не можу змусити цього працювати. find ~/Projects -name '*.js' -\! -name 'node_modules' -pruneвсе ще з'являється файли з node_modulesна своєму шляху
mpen

1
@mpen, Від stackoverflow.com/questions/4210042 / ... , я дізнався , що синтаксис ви хочете find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Ім'я цього шляху має точно відповідати знаходженню, яке буде надруковано, якби він збирався надрукувати каталог.
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, це кількість записів у каталозі, яку він повинен вивчити. Так що набагато гірше, якщо у вас багато, багато маленьких файлів (особливо, якщо вони всі помножені між собою!), Ніж якщо у вас є лише кілька гігабайтних файлів.
Toby Speight

@TobySpeight: хороший момент. Я згадав розмір простору пошуку для позначення масштабу, який також містить багато файлів. Швидкий пошук root (/) із sudo ls -R / | wc -lвказівками ~ 76.5M файлів (більшість з яких резервного копіювання, за винятком системних файлів "неконфігурованих"); /mnt/Vancouver/з ls -R | wc -lвказує ~ 2,35М файлів; /home/victoria/містить 0,668M файлів.
Вікторія Стюарт

4

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

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.
Аксель Беккерт

ммм, подивимось? може, тому, що я спробував їх усіх?
Шестеро

знайти скрізь однакову реалізацію для всіх дистрибутивів 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

Може пройти більше, ніж зазначений каталог.
МУП Бельгія

2

як використовувати-чорнослив-варіант-знайти-в-ш - чудова відповідь Лоренса Гонсальвеса про те, як -pruneпрацює.

І ось загальне рішення:

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/… я дізнався, що синтаксис, який використовується для -pathmust, повинен відповідати імені, яке знайде, було б надрукувати, якби воно було, наприклад, для надрукування каталогу 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 іректорії в/

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 та виключати (не звичайний файл) та виключати вихід 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 Я збирався відредагувати свою публікацію, щоб відобразити ваш коментар, але після швидкого тестування (без цитування, {}чи працює файли з пробілами у своїх іменах) та перегляду сторінок man, здається, що цитування потрібно лише уникати їх неправильно трактувати як розділові знаки сценарію оболонки. У цьому випадку ви б використовували одне цитування:'{}'
Альберто

Я думаю , що я повинен був використовувати це зробити cpабо mvчи rm. Я перевірю це
Людовик Куті

1

Я спробував команду вище, але жоден із тих, хто використовує "-prune", не працює для мене. Врешті я спробував це за допомогою команди нижче:

find . \( -name "*" \) -prune -a ! -name "directory"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.