find: prune не игнорирует указанный путь

13

Мне нужно исключить .gitиз моего findпоиска. Чтобы добиться этого, я использую -path ./.git -pruneпереключатель:

$ find . -path ./.git -prune -o \( -type f -o -type l -o -type d \) | grep '.git'
./.git

Однако, хотя при этом пропускается содержимое каталога .git, в нем указывается сам каталог. Работает когда добавляю-path ./.git -prune -o -print -a

find . -path ./.git -prune -o -print -a \( -type f -o -type l -o -type d \) | grep '.git'

Зачем это нужно? Я думал, что обе команды должны иметь одинаковый вывод. Второй синтаксис довольно уродлив.

Мартин Вегтер
источник
4
Вам нужно -print, иначе неявное -printотносится ко всему условию
Стефан Шазелас
1
См. Также unix.stackexchange.com/q/102191/22565
Стефан

Ответы:

3

manСтраница findдает:

-prune True; if the file is a directory, do not  descend  into  it.  If
      -depth  is  given,  false;  no  effect.  Because -delete implies
      -depth, you cannot usefully use -prune and -delete together.

Таким образом, в первом примере это не так, и -path ./.git -pruneэто не соответствует действительности, и поэтому действие по умолчанию ( -print) не будет вызываться, поэтому строка печатается.

Timo
источник
22

Я был озадачен тем, почему findкоманда также печатала обрезанные каталоги , и некоторыми другими запутанными подробностями того, как это -pruneработает, но смог выяснить это на нескольких примерах.

Для запуска приведенных ниже примеров создайте следующие каталоги и файлы.

mkdir aa
mkdir bb
touch file1
touch aa/file1
touch bb/file3

Чтобы создать эту структуру:

$ tree

.
├── aa
   └── file1
├── bb
   └── file3
└── file1

Теперь используйте find для поиска каталогов с именем aa. Здесь нет проблем.

$ find . -type d -name aa
./aa

Ищите все каталоги, кроме aa, и мы получим текущий каталог .и ./bb, что также имеет смысл.

$ find . -type d ! -name aa
.
./bb

Пока все хорошо, но когда мы используем -prune, find возвращает каталог, который мы удаляем, что поначалу меня смутило, потому что я ожидал, что он вернет все остальные каталоги, а не тот, который был удален.

$ find . -type d -name aa -prune
./aa

Причина, по которой он возвращает удаляемый каталог, объясняется не в -pruneразделе справочных страниц, как указано в ответе Тимо , а в EXPRESSIONSразделе:

Если выражение не содержит никаких действий, кроме -prune,  -printвыполняется для всех файлов, для которых выражение является истинным.

Это означает, что, поскольку выражение совпадает с aaименем каталога, выражение будет иметь значение true и будет напечатано, потому что find неявно добавляет a -printв конце всей команды. Однако это не добавит -print, если вы намеренно добавили действие -o -printв конец самостоятельно:

find . -type d -name aa -prune -o -print
.
./file1
./bb
./bb/file3

Здесь команда find больше НЕ добавляет неявное -print, и поэтому каталог, который мы отбрасываем ( aa), не будет напечатан.

Итак, наконец, если мы добавим предложение, которое ищет файлы с шаблоном имени файла file*после -o, то вы должны поставить -printв конце этого второго предложения, как это:

find . \( -type d -name aa -prune \) -o \( -type f -name 'file*' -print \)
./file1
./bb/file3

Причина, по которой это работает, одна и та же: если вы не поместите a -printво второе предложение, то, поскольку нет действия, кроме -pruneдействия, find автоматически добавит -printв конце команды END, в результате чего -pruneпредложение напечатает подрезанный каталог:

find . \( \( -type d -name aa -prune \) -o \( -type f -name 'file*' \) \) -print
./aa
./file1
./bb/file3

В общем, вам нужно поместить -printкоманду во втором предложении. Если вы поместите его посередине, как это делал оригинальный постер, он не будет работать правильно, потому что удаляемые файлы будут распечатаны немедленно, а во втором предложении не будет возможности выбрать нужные файлы:

find . \( -type d -name aa -prune -o -print \) -o \( -type f -name 'file*' \)
.
./file1
./bb
./bb/file3

Так что, к сожалению, оригинальный постер неправильно дал команду выше, поместив ее -printв неправильном месте. Это могло бы сработать для его конкретного случая, но в общем случае это не сработало.

Есть тысячи людей, которым трудно понять, как -pruneработает. Страница findman должна быть обновлена, чтобы предотвратить бесконечную путаницу во всем мире по поводу этой команды.

Джерри Марбас
источник
Моя страница find-4.6.0руководства (Arch Linux ) говорит: «Если все выражение не содержит никаких действий, кроме -prune или -print, -print выполняется для всех файлов, для которых все выражение истинно». Но работает как вы описываете.
x-
0

со manстраницы,

Чтобы игнорировать все дерево каталогов, используйте -prune вместо проверки каждого файла в дереве. Например, чтобы пропустить каталог `src / emacs 'и все файлы и каталоги в нем, и напечатать имена других найденных файлов, сделайте что-то вроде этого:

find . -path ./src/emacs -prune -o -print

Итак, может быть, вы можете использовать:

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

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

чтобы указать имя файла, вы можете сделать так:

find . -path ./.git -prune , -name filename

Пожалуйста, обратите внимание на ,запятую оператора.

jianyongli
источник
-1

Вот быстрая и грязная альтернатива:

find | fgrep -v /.git

Я просто не могу вспомнить findсложный синтаксис ...

peter.slizik
источник