Как использовать '-prune' вариант 'find' в sh?
Я не совсем понимаю пример, приведенный в man find
, кто-нибудь может дать мне несколько примеров и объяснений? Могу ли я объединить в нем регулярное выражение?
Более подробный вопрос такой:
Напишите сценарий оболочки changeall
, который имеет интерфейс, подобный changeall [-r|-R] "string1" "string2"
. Он найдет все файлы с суффиксом .h
, .C
, .cc
или .cpp
и изменит все вхождения string1
на string2
. -r
- это опция, позволяющая оставаться только в текущем каталоге или в том числе в дополнительном каталоге.
ПРИМЕЧАНИЕ:
- В нерекурсивном случае
ls
НЕ допускается, мы можем использовать только find
и sed
.
- Я попробовал
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]
.
Важные замечания:
-
Если все, что вы хотите сделать, это распечатать результаты, которые вы могли бы использовать для -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
-
Если ваше "обычное условие" совпадает с файлами, которые также соответствуют вашему состоянию обрезки, эти файлы не будут включены в выходные данные. Это можно исправить, -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
.