Запустіть `grep`, виключаючи файл у певному шляху


12

Я хочу виключити файл ./test/main.cppіз мого пошуку.

Ось що я бачу:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Я знаю, що можна отримати результат, який я хочу, скориставшись декількома командами в розташуванні "труби і фільтри", але чи є якесь цитування / уникнення, яке дасть grepзрозуміти, що я хочу спочатку?


Рішення, засноване на фільтрації виводу, не набирає масштабів, оскільки воно без потреби шукає файл, перш ніж виключати пов'язані результати. Питання збільшується, якщо я хочу виключити цілі каталоги (з --exclude-dir). Ось чому я хотів би змусити греп виконувати виключення на місцях.
нобар

1
--exclude вказує глобус не шлях
PersianGulf

Відповіді:


6

grep не вдається зробити це для файлу в одному певному каталозі, якщо у вас є кілька файлів з тим самим іменем у різних каталогах, скористайтеся функцією find замість:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


Чому ви уникнути \!і \+? Здається, добре спрацьовує без нахилів.
nobar

@nobar Я звик до цього, тому що деякі символи є ключовими словами оболонки, тому ви ніколи не здивуєтесь, тому що нічого не може статися, якщо їх уникнути.
MichalH

" grepне можу цього зробити, використовуй findнатомість" - ідеально.
nobar

4

Я не думаю, що це можливо з GNU grep. Вам не потрібні труби.

З find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

З zsh:

grep pattern ./**/*~./test/main.cpp(.)

(виключає приховані файли, так само як і для виключення .git, .svn ...).


2

Я міг би написати книгу: "Втрачене мистецтво xargs". У find ... -exec … ';запускаєте GREP для кожного файлу (але варіант з -exec … +робить). Що ж, ми витрачаємо цикли процесора сьогодні, чому б ні, правда? Але якщо продуктивність, пам'ять та потужність - це проблема: використовуйте xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU find''s -print0буде NULприпиняти свій вихід і xargs' -0параметр відзначає цей формат як вхідний. Це гарантує будь-які смішні символи у вашому файлі, конвеєр не заплутається. Ця -rопція гарантує відсутність помилок у випадку, якщо findнічого не знайдено.

Зауважте, тепер ви можете робити такі речі:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep's -zробить те саме, що і xargs ' -0.


3
Кілька цікавих записок, але я не впевнений, що ви правильні щодо продуктивності. Як я розумію, це find -exec (cmd) {} +працює так само, як xargsі find -exec (cmd) {} \;працює так само, як xargs -n1. Іншими словами, ваше твердження правильне, лише якщо використовується \;версія.
nobar

3
Передача даних xargsменш ефективна, ніж використання -exec … +(хоча і незначно). Жодної з відповідей тут навіть не згадується -exec … \;.
Жиль "ТАК - перестань бути злим"

1
Ну, с - т. Я побачу себе. Дякуємо за коментарі та виправлення. Я думав, що \ + - друкарська справа. О, дивись, -exec ... +додано в січні 2005 р. Так, я не застарів ... взагалі ... все.
Отей

2

Якщо ваша findпідтримка, -pathяка була додана до POSIX у 2008 році, але все ще відсутня в Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
Я не думаю, що це спрацює, тому що nobar хоче main.cpp в інших каталогах
Ерік Реноф

1
чи ваш шаблон також не виключає main.cpp з усіх інших каталогів? Це було б не бажано
Ерік Реноф

@EricRenouf: О, моя помилка, неправильне читання. Оновлено мою відповідь.
cuonglm

@Gilles: Чому -pathце не POSIX?
cuonglm

Ах, вибачте, моя помилка, вона була додана у 2008 році. Досі відсутня у Solaris.
Жил "ТАК - перестань бути злим"

1

Для запису ось такий підхід, який я віддаю перевагу:

grep pattern $(find . -type f ! -path './test/main.cpp')

Зберігаючи grepна початку команди, я думаю, що це трохи зрозуміліше - плюс це не вимикає grepкольорове виділення. У певному сенсі використання findв підстановці команд - це лише спосіб розширення / заміни (обмеженого) підмножини пошуку файлів grep.


Для мене find -execсинтаксис є своєрідним таємничим. Однією із складнощів find -execє (іноді) потреба в уникненні різних символів (особливо, якщо \;використовується під Bash). Наступні дві команди є лише еквівалентними для цілей уведення звичних контекстів:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

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

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Ще одна примітка для узагальнення findрішень на основі базування для використання в скриптах : grepКомандний рядок повинен містити -H/ --with-filenameпараметр. Інакше це змінить формат виводу за умови, що в результатах пошуку буде лише одне ім'я файлу find. Це помітно, оскільки він, здається, не потрібен, якщо використовується grepвласний пошук файлів (з -rопцією).

... Ще краще - включити /dev/nullяк перший файл для пошуку. Це вирішує дві проблеми:

  • Це гарантує, що якщо є один файл для пошуку, grepподумає, що є два, і використовувати режим виводу з декількома файлами.
  • Він гарантує, що якщо файлів для пошуку не grepбуде , буде думати, що є один файл, і не зачекає на stdin.

Отже, остаточна відповідь:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

Ви не повинні використовувати результат findв підстановці команд. Це порушується, якщо є імена файлів, що містять пробіли чи інші спеціальні символи. Використовується find -exec, він надійний і простий у використанні.
Жил "ТАК - перестань бути злим"

@Gilles: Дуже хороший момент - також вихід може перевищувати обмеження розміру командного рядка деяких програм. Caveat emptor.
nobar

Тьфу. «знайти» синтаксис страшенно складно. '-o' є оператором "або" (також "-or" в Linux), але типове використання (наприклад, з "-prune") концептуально не відображає поняття логічного або. Це функціональний, а не логічний або.
nobar

Інший спосіб , щоб виключити підкаталоги , засновані на збігу імені: find -iname "*target*" -or -name 'exclude' -prune. Ну, це працює, тому що - обрізаний каталог буде вказаний, але не шуканий. Якщо ви не хочете, щоб його було вказано у списку, ви можете додати щось надлишкове! -name 'exclude'
nobar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.