Як використовувати опцію '-prune' в 'sh'?


219

Я не зовсім розумію приклад, поданий з man find, може хтось надати мені кілька прикладів та пояснень? Чи можна поєднувати в ньому регулярне вираження?


Більш детальне запитання таке:

Напишіть скрипт оболонки changeall, у якого подібний інтерфейс changeall [-r|-R] "string1" "string2". Він знайде всі файли з суфіксом .h, .C, .ccабо .cppі змінити всі входження string1в string2. -rє варіантом для перебування лише в поточному режимі або включаючи субдір.

ПРИМІТКА:

  1. Для рекурсивного випадку lsНЕ дозволено, ми могли використовувати лише findта sed.
  2. Я спробував, find -depthале це НЕ підтримувалося. Тому мені було цікаво, чи -pruneможна допомогти, але не зрозумів приклад з man find.

EDIT2: Я робив завдання, я не задавав питання дуже детально, тому що хотів би закінчити його сам. Оскільки я це вже зробив і передав, зараз можу викласти все питання. Також мені вдалося виконати завдання без використання -prune, але хотілося б навчитися його.

Відповіді:


438

Те, про що я заплутався, -prune- це те, що це дія (як -print), а не тест (як -name). Він змінює список "завдання", але завжди повертає істину .

Загальна схема використання -pruneтака:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

Ви майже завжди хочете, щоб -o(логічний АБО) відразу після цього -prune, тому що перша частина тесту (аж до включно -prune) поверне помилкові речі, які ви насправді хочете (тобто: речі, які ви не хочете обрізати).

Ось приклад:

find . -name .snapshot -prune -o -name '*.foo' -print

Тут ви знайдете файли "* .foo", які не знаходяться в каталогах ".snapshot". У цьому прикладі -name .snapshotскладається [conditions to prune], і -name '*.foo' -printє, [your usual conditions]і [actions to perform].

Важливі примітки :

  1. Якщо все, що ви хочете зробити, це надрукувати результати, які можуть бути використані для виходу з -printдії. Зазвичай ви не хочете цього робити під час використання -prune.

    Типова поведінка знаходження за замовчуванням - це "і" весь вираз із -printдією, якщо -pruneв кінці немає інших дій, крім (за іронією долі). Це означає, що писати це:

    find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    рівнозначно написанню цього:

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

    а це означає, що він також роздрукує ім'я каталогу, який ви обрізаєте, що зазвичай не те, що ви хочете. Натомість краще чітко вказати -printдію, якщо це те, що ви хочете:

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. Якщо ваш "звичайний стан" відповідає файлам, які також відповідають вашим умовам обрізки, ці файли не будуть включені у висновок. Спосіб виправити це - додати -type dприсудок до вашої умови чорносливу.

    Наприклад, припустимо, що ми хотіли вирізати будь-який каталог, який розпочався з .gitцього (це, правда, дещо надумано - зазвичай вам потрібно видалити річ, названу саме так .git ), але крім цього хотілося переглянути всі файли, включаючи файли на зразок .gitignore. Ви можете спробувати це:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    Це не буде включатись .gitignoreу висновок. Ось виправлена ​​версія:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

Додаткова порада: якщо ви використовуєте версію GNU find, на сторінці texinfo для сторінки findє більш детальне пояснення, ніж її сторінка (як це стосується більшості утиліт GNU).


6
це не на 100% очевидно у вашому тексті (але оскільки ви друкуєте лише "* .foo", це не суперечить), але частина -prune також не буде друкувати нічого (не лише каталоги) під назвою ".snapshot". тобто -pruneпрацює не лише в каталогах (але для каталогів це також перешкоджає введенню в каталоги, які відповідають цій умові, тобто тут dirs відповідають цьому -name .snapshot).
Олів'є Дулак

12
і +1 для вас за добре зроблене пояснення (і особливо важливу ноту). Ви повинні подати це розробникам знахідок (оскільки сторінка man не пояснює "чорнослив" для нормальних людей ^^ У мене знадобилося багато спроб розібратися, і я не побачив того побічного ефекту, про який ви нас попереджаєте)
Олів'є Дулак

2
@OlivierDulac Це дуже хороший момент щодо потенційного відключення файлів, які ви хочете зберегти. Я оновив відповідь, щоб уточнити це. -pruneДо речі, це насправді не саме це. Проблема полягає в тому, що або оператор "коротке замикання", або має нижчий пріоритет ніж та. Кінцевий результат полягає в тому, що якщо .snapshotзустрічається викликаний файл , він буде відповідати першому -name, -pruneтоді нічого не зробить (але поверне true), а потім і поверне true, оскільки його лівий аргумент був істинним. Дія (наприклад:) -printє частиною другого аргументу, тому вона ніколи не має шансу виконати.
Лоранс Гонсальвес

3
+1 нарешті з’ясував, навіщо мені потрібно -printв кінці кінців, тепер я можу припинити додавати \! -path <pattern>додатково до-prune
Жалюгідної змінної

6
Зауважте, що "-o" - це скорочення для "-or", яке (хоча не сумісне з POSIX) читає чіткіше.
yoyo

27

Зазвичай нашим способом ми робимо речі в Linux і так, як ми думаємо, зліва направо.
Отже, ви б пішли і писали, що спочатку шукаєте:

find / -name "*.php"

Тоді ви, мабуть, натисніть клавішу Enter і зрозумієте, що отримуєте занадто багато файлів із каталогів, яких ви не хочете. Виключіть / медіа, щоб уникнути пошуку встановлених накопичувачів.
Тепер слід просто ДОПУСТИТИ наступне до попередньої команди:

-print -o -path '/media' -prune

тому остаточна команда:

find / -name "*.php" -print -o -path '/media' -prune

............... | <--- Включити ---> | .................... | <- -------- Виключити ---------> |

Я думаю, що ця структура набагато простіша і корелює з правильним підходом


3
Я б не очікував, що це буде ефективним - я б подумав, що він спочатку оцінить ліву пропозицію перед чорносливом, але, на мій подив, швидкий тест, начебто, підказує, що findдосить розумний, щоб -pruneспершу обробити цю пропозицію. Хммм, цікаво.
artfulrobot

Я ніколи не вважав, що за майже десятиліття використання GNU find! Дякую тобі за це! Це, безумовно, змінить те, про що я думаю -pruneвідтепер.
Феліпе Альварес

3
@artfulrobot Це справді спочатку його обробляти? Я б подумав, що це входить /media, помітивши, що його не називають, *.phpа потім перевіряє, чи є він зараз усередині /media, побачивши, що він є, і тому пропускає все це піддерево. Це все ще зліва направо, це просто не має значення, якщо обидва чека не перетинаються.
phk

26

Будьте уважні, що -prune не заважає спускатися в будь-який каталог, як дехто сказав. Це не дозволяє спускатися в каталоги, які відповідають тесту, до якого застосовано. Можливо, деякі приклади допоможуть (див. Приклад регексу внизу). Вибачте за те, що це так довго.

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

якщо ви «помацати ./dir1/scripts/test» (тобто, є файл «тест», а не реж, в тому , що роздрукований підкаталозі), він не буде отримувати printd по find . -name test -prune -o -print: IOW, -pruneце дія , яка також працює над файлами
Олів'є Дулак

10

Додавання до порад, наданих в інших відповідях (у мене немає відповіді для створення відповідей) ...

При поєднанні -pruneз іншими виразами є тонка різниця в поведінці залежно від того, які інші вирази використовуються.

Приклад @Laurence Gonsalves знайде файли "* .foo", які не входять у каталоги ".snapshot": -

find . -name .snapshot -prune -o -name '*.foo' -print

Однак цей дещо інший короткий .snapshotрукав , можливо, ненароком, також перелічить каталог (та будь-які вкладені каталоги .snapshot): -

find . -name .snapshot -prune -o -name '*.foo'

Причина полягає в тому, що (за даними сторінки моєї системи): -

Якщо даний вираз не містить жодного з праймерів -exec, -ls, -ok або -print, даний вираз ефективно замінюється на:

(заданий вираз) -друк

Тобто другий приклад є еквівалентом введення наступного, тим самим змінюючи групування термінів: -

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

Принаймні, це було помічено на Solaris 5.10. Використовуючи різні аромати * nix протягом приблизно 10 років, я нещодавно шукав причину, чому це відбувається.


Дякуємо, що звернули увагу на різницю між використанням -pruneта без -print!
mcw

3

Prune - це не повторення при будь-якому перемиканні каталогу.

Зі сторінки чоловіка

Якщо -depth не задано, правда; якщо файл - каталог, не спускайтеся в нього. Якщо -depth задано, false; ніякого ефекту.

В основному він не скасовується в жодних підкаталогах.

Візьмемо цей приклад:

У вас є такі каталоги

  • / home / test2
  • / home / test2 / test2

Якщо ви запускаєте find -name test2:

Він поверне обидва каталоги

Якщо ви запускаєте find -name test2 -prune:

Він повернеться лише / home / test2, оскільки не спуститься в / home / test2, щоб знайти / home / test2 / test2


не на 100% правда: це "зробіть обрізку при відповідності умові, і якщо це каталог, вийміть його зі списку справ, тобто не введіть його також". -prune також працює на файли.
Олів'є Дулак

2

Я не є експертом з цього питання (і ця сторінка була дуже корисною разом з http://mywiki.wooledge.org/UsingFind )

Щойно помічено -path, це шлях, який повністю відповідає рядку / шляху, що йде одразу післяfind ( .у прикладах тез), де він -nameвідповідає всім базовим іменам.

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

блокує каталог .git у вашому поточному каталозі ( як ваш результат у . )

find . -name .git  -prune -o -name file  -print

рекурсивно блокує всі підкаталоги .git.

Зауважте, ./ це надзвичайно важливо !! -pathповинен відповідати шляху, прив’язаному до того . чи іншого, що з’являється відразу після пошуку, якщо ви отримаєте збіги з ним (з іншої сторони або ' -o'), ймовірно, його не обрізають! Я наївно не знав цього, і це змусило мене використовувати -ath, коли це здорово, коли ви не хочете обрізати всі підкаталоги з тим самим базовим іменем: D


Зверніть увагу, якщо ви говорите, find bla/тоді вам знадобиться -ath bla/.git (або якщо ви засунули a *на фронті, то він буде поводитись більше як -name)
sabgenton

1

Покажіть усе, включаючи сам dir, але не його довгий нудний вміст:

find . -print -name dir -prune

0

Якщо ви прочитаєте всі хороші відповіді тут, то зараз я розумію, що наступні результати приносять однакові результати:

find . -path ./dir1\*  -prune -o -print

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

find . -path ./dir1\*  -o -print
#look no prune at all!

Але останній триватиме набагато більше часу, оскільки він все ще шукає все в dir1. Я думаю, що справжнє питання полягає в тому, як -orвідшукати небажані результати, фактично не шукаючи їх.

Тож я гадаю, що чорнослив означає недостойні минулі матчі, але відзначити його як закінчене ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "Однак це не пов'язано з дією" -prune "(що лише запобігає подальшому спуску. Це не впевнено ми ігноруємо цей елемент). Натомість цей ефект обумовлений використанням "-o". Оскільки ліва частина умови "або" досяг успіху ./src/emacs, оцінювати правий- не потрібно сторона ('-print') взагалі для цього конкретного файлу. "


0

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

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

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

Все -pruneце - це змінити поведінку пошуку. Якщо поточна відповідність є каталогом, вона говорить "ей find, той файл, який ви щойно зіставили, не спускайтеся в нього" . Він просто видаляє це дерево (але не сам файл) зі списку файлів для пошуку.

Його слід назвати -dont-descend.


0

Відповідей досить багато; деякі з них трохи надто важкі для теорії. Я залишу, чому мені потрібен був чорнослив один раз, тому, можливо, пояснення " потрібне перше / приклад" комусь корисне :)

Проблема

У мене була папка з приблизно 20 вузлами каталогів, кожен з яких мав свій node_modulesкаталог, як очікувалося.

Як тільки ви потрапляєте в будь-який проект, ви бачите кожного ../node_modules/module. Але ви знаєте, як це. Практично кожен модуль має залежності, тому те, що ви дивитесь, більше схожеprojectN/node_modules/moduleX/node_modules/moduleZ...

Я не хотів заглушатися списком із залежністю залежності ...

Знаючи -d n/ -depth n, це не допомогло б мені, оскільки головний / перший каталог node_modules, який я хотів для кожного проекту, був на різній глибині, як це:

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

Як я можу отримати перший список шляхів, що закінчуються на першому, node_modulesі перейти до наступного проекту, щоб отримати той самий?

Введіть -prune

Коли ви додасте -prune, у вас все ще буде стандартний рекурсивний пошук. Кожен "шлях" аналізується, і кожна знахідка випльовується і findпродовжує копати, як хороший хлопець. Але це копання для більшого, node_modulesчого я не хотів.

Таким чином, різниця в тому , що в будь-якому з цих різних шляхів, -pruneбуде findзупинити копати далі вниз , що конкретний проспект , коли він знайшов вашу деталь. У моєму випадку node_modulesпапка.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.