знайти: чорнослив не ігнорує вказаний шлях


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стосується всієї умови
Стефана Шазеласа


Відповіді:


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) не називатиметься, отже, рядок друкується.


22

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

Для запуску наведених нижче прикладів створіть наступні каталоги та файли.

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

Щоб створити цю структуру:

$ tree

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

Тепер використовуйте пошук для пошуку каталогів з назвою aa. Тут немає жодних проблем.

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

Шукайте всі каталоги, крім aa, і ми отримуємо поточний каталог .і ./bb, що також має сенс.

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

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

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

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

Якщо вираз не містить жодних інших дій, крім цього -prune,  -printвиконується для всіх файлів, для яких вираз є істинним.

що означає, що оскільки вираз відповідає імені aaкаталогів, то вираз буде оцінено як істинне і воно буде надруковано, оскільки пошук неявно додає а -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команду THE END в команду 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працює. findСторінку " man" слід оновити, щоб уникнути невпевненої світової плутанини щодо цієї команди.


Моя сторінка man (Arch Linux, find-4.6.0) говорить: "Якщо весь вираз не містить жодних дій, крім -prune або -print, -print виконується на всіх файлах, для яких цілий вираз є істинним". Але працює, як ви описуєте.
x-yuri

0

зі manсторінки,

Щоб ігнорувати ціле дерево каталогів, використовуйте -pnene, а не перевіряючи кожен файл у дереві. Наприклад, щоб пропустити каталог `src / emacs 'та всі файли та каталоги під ним та надрукувати імена інших знайдених файлів, зробіть щось подібне:

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

Отже, можливо, ви можете використовувати:

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

Це не буде перелічити .gitкаталог.

щоб вказати ім'я файлу, ви можете зробити так:

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

Зверніть увагу на ,оператора комами.


-1

Ось швидка та брудна альтернатива:

find | fgrep -v /.git

Я просто не можу згадати findскладний синтаксис ...

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