Как использовать '-prune' вариант 'find' в sh?

Я не совсем понимаю пример, приведенный в 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.

ОБНОВЛЕНИЕ 2: Я делал задание, я не задавал вопрос в больших деталях, потому что я хотел бы закончить его сам. Поскольку я уже сделал это и сдаю, теперь я могу сформулировать весь вопрос. Кроме того, мне удалось выполнить задание без использования -prune, но я бы все равно хотел его изучить.

Ответ 1

В -prune то, что это действие (например, -print), а не тест (например, -name). Он изменяет список "дел", но всегда возвращает true.

Общая схема использования -prune такова:

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

Вы почти всегда хотите -o (логическое ИЛИ) сразу после -prune, потому что эта первая часть теста (вплоть до -prune) вернет false для того, что вам действительно нужно (то есть того, что вы не хочу сокращать).

Вот пример:

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 действия -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 предикат -type d к состоянию чернослива.

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

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

    Это не будет включать .gitignore в выводе. Вот исправленная версия:

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

Дополнительный совет: если вы используете GNU-версию find, страница texinfo для find имеет более подробное объяснение, чем ее man-страница (как это верно для большинства утилит GNU).

Ответ 2

Остерегайтесь того, что -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

Ответ 3

Как правило, мы делаем то, что мы делаем в Linux, и как мы думаем, это слева направо. Итак, вы пойдете и напишите, что ищете, прежде всего:

find / -name "*.php"

Затем вы, вероятно, нажмете enter и поймете, что получаете слишком много файлов из каталоги, которых вы не хотите. Позвольте исключить /media, чтобы избежать поиска на установленных дисках.
Теперь вы должны просто добавить к предыдущей команде следующее:

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

поэтому последняя команда:

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

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

Я думаю, что эта структура намного проще и соотносится с правильным подходом

Ответ 4

Добавление к советам, приведенным в других ответах (у меня нет ответа на создание ответов)...

При объединении -prune с другими выражениями существует тонкая разница в поведении, в зависимости от того, какие другие выражения используются.

Пример

@Laurence Gonsalves найдет файлы "*.foo", которые не находятся в каталогах ".snapshot": -

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

Однако эта немного другая короткая команда, возможно, непреднамеренно, также перечислит каталог .snapshot (и любые вложенные каталоги .snapshot): -

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

Причина (согласно man-странице моей системы): -

Если данное выражение не содержит никаких праймеров -exec, -ls, -ok или -print, данное выражение эффективно заменяется на:

(given_expression) -print

То есть второй пример эквивалентен вводу следующего, тем самым изменяя группировку терминов: -

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

Это, по крайней мере, было замечено в Solaris 5.10. Используя различные вкусы * nix в течение приблизительно 10 лет, я только недавно искал причину, почему это происходит.

Ответ 5

Чернослив - это не рекурсия при любом переключении каталога.

На странице man

Если -depth не задано, true;       если файл является каталогом, не спускайтесь в него.   Если задано -depth, false; никакого эффекта.

В принципе, он не будет входить в какие-либо подкаталоги.

Возьмите этот пример:

У вас есть следующие каталоги

  • /дома/test2
  • /дома/test2/test2

Если вы запустите find -name test2:

Он вернет обе директории

Если вы запустите find -name test2 -prune:

Он вернется только /home/test 2, так как он не спустится в /home/test 2, чтобы найти/home/test2/test2

Ответ 6

Я не эксперт в этом (и эта страница была очень полезна вместе с 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" ), вероятно, не будет обрезают! Я наивно не знал об этом, и это заставило меня использовать -path, когда это здорово, когда вы не хотите обрезать весь подкаталог с одним и тем же базовым именем: D

Ответ 7

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

find . -print -name dir -prune

Ответ 8

Если вы прочтете все хорошие ответы, то теперь я понимаю, что следующие результаты возвращают те же результаты:

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 action "(который только предотвращает дальнейшее спуск, он не гарантирует, что мы игнорируем этот элемент). Вместо этого этот эффект обусловлен использованием" -o. левая сторона условия "или" преуспела для. /src/emacs, нет необходимости оценивать правую часть ( "-print" вообще для этого конкретного файла ".

Ответ 9

find создает список файлов. Он применяет каждый предикат, который вы указали, и возвращает пропущенные.

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

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

Все, -prune делает -prune, это изменяет поведение поиска. Если текущее совпадение является каталогом, оно говорит: "Эй, find, тот файл, который ты только что сопоставил, не спускайся в него". Он просто удаляет это дерево (но не сам файл) из списка файлов для поиска.

Он должен быть назван -dont-descend.

Ответ 10

Есть довольно много ответов; некоторые из них слишком сложны для теории. Я оставлю почему мне нужно было обрезать один раз, так что, может быть, объяснение need-first/example кому-то пригодится :)

Проблема

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

Как только вы входите в любой проект, вы видите каждый ../node_modules/module. Но вы знаете, как это. Почти каждый модуль имеет зависимости, поэтому то, на что вы смотрите, больше похоже на projectN/node_modules/moduleX/node_modules/moduleZ...

Я не хотел тонуть со списком с зависимостью от зависимости...

Зная -d n/-depth n, это не помогло бы мне, так как каталог main/first 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.