Как исключить некоторые файлы, не соответствующие определенным расширениям, с помощью grep?

8

Я хочу вывести все строки, содержащие слово OKрекурсивно из каталога. Но есть несколько расширений, которые мне нужно исключить из результата:

*~
*.map
*.js except *.debug.js

Я старался:

grep -r --exclude={*~,*.map} "OK" /some/dir

За исключением того, что я не знаю, как удалить из результата все эти не отладочные .jsфайлы.

Вопрос переполнен
источник

Ответы:

7

Я бы просто пропустил это через секунду, grepчтобы удалить их:

grep -r --exclude={\*~,\*.map} "OK" bar/ | grep -vP '(?<!debug)\.js'

-vПереворачивает матч, печать строки , которые не соответствуют шаблону и -Pпозволяет Perl Compatible Regular Expressions , которые позволяют нам использовать отрицательные Утверждения назад . Это конкретное регулярное выражение будет соответствовать .jsтому, чему не предшествует то, debugкаким образом (поскольку мы инвертируем совпадения) .jsбудут напечатаны только эти файлы.

Однако, как указал @QuestionOverflow в комментариях, это может привести к непреднамеренному побочному эффекту отфильтровывания строк , содержащих OKи jsтак как grep -vприменяется ко всему выводу, а не только к имени файла. Чтобы избежать этого, просто добавьте двоеточие (это то, что grepиспользуется для отделения имен файлов от содержимого файлов):

grep -r --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js:'

Это все равно не удастся, если ваша строка ввода содержит foo.js:или если ваше имя файла содержит :. Итак, чтобы быть уверенным, используйте другой подход:

grep -Tr --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js\t'

-TПриводит grepк печати вкладки между именем файла и содержимое файла. Итак, если мы просто добавим a \tв конец регулярного выражения, оно будет сопоставляться только с именами файлов, а не с содержимым строки.

Тем не менее, использованиеfind может иметь больше смысла независимо.

Тердон
источник
1
Буду ли я непреднамеренно исключать строки в тех файлах, которые мне нужны, но которые содержат оба OKи .jsв одной строке?
Переполнение вопроса
@QuestionOverflow ах, да, действительно, хороший улов. Смотрите обновленный ответ.
Тердон
Фантастический ответ. Я должен принять твое, потому что я прошу специально для grep. Спасибо.
Переполнение вопроса
@QuestionOverflow, пожалуйста. В целом, хотя, findвероятно, лучше для такого рода вещей. Получение права grepможет быть сложно, как вы указали :).
Тердон
Ваши решения потерпят неудачу, если failglobв оболочке установлена ​​опция: bash: no match: --exclude=*~ вам нужно заключить в кавычки аргументы шаблона GLOB, --excludeчтобы скрыть их от расширения оболочки, например--exclude={\*~,\*.map}
Ian D. Allen
7

Я бы использовал, findчтобы найти файлы и передать результат через xargs:

$ find . -type f \! -name "*~" \
                 \! -name "*.map" \
                 \! \( -name "*.js" -and \! -name "*.debug.js" \) \
         -print0 | xargs -0 grep "OK"

Это ищет каждый файл, не соответствующий " *~", " *.map" или " *.jsно не *.debug.js".

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

Андреас Визе
источник
Хороший ответ тоже :)
переполнение вопроса
3
Да, это, вероятно, лучший способ, +1. Вы также можете использовать -exec grep OK {} +вместо xargsи избежать дополнительной программы.
Тердон
2
@IDAllen нет, обратите внимание, что я предложил -exec +нет -exec \;, так как будет запускаться как можно меньше команд, очень похоже xargs.
Тердон
4

С zshвами можно сделать:

setopt extendedglob
grep OK some/dir/**/^(*~|*.map|(^*debug).js)

При условии, конечно, список аргументов не слишком длинный, и в этом случае вы всегда можете сделать:

printf '%s\0' some/dir/**/^(*~|*.map|(^*debug).js) | xargs -0 grep OK
Graeme
источник
Кроме того, вы можете сделать последний-только zsh: autoload zargsиzargs some/dir/**/^(*~|*.map|(^*debug).js) -- grep OK
don_crissti
2

Если вы не возражаете против того, чтобы увидеть результат немного не в порядке (если вы это сделаете, вы можете отсортировать его):

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir **/*.debug.js

Для этого требуется, чтобы ваша оболочка поддерживала **рекурсивное сглаживание: zsh работает «из коробки», bash - после запуска shopt -s globstar, ksh93 - после запуска set -o globstar.

Без **поддержки в оболочке вы можете использовать две команды grep:

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir
grep -r --include=*.debug.js "OK" /some/dir
Жиль "ТАК - перестань быть злым"
источник
Моя оболочка поддерживает **, но, похоже, что-то не так с дополнительным аргументом **/*.debug.js, в результате чего grep интерпретируется OKкак каталог. Вы пытались запустить его?
Переполнение вопроса
@QuestionOverflow Моя ошибка, я поменял местами аргументы.
Жиль "ТАК - перестань быть злым"
2

Вы можете использовать ripgrep. По умолчанию он игнорирует скрытые файлы и уважает ваш .gitignoreфайл.

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

-g/ --glob GLOBВключить или исключить файлы и каталоги для поиска, которые соответствуют заданному глобу.

-t/ --type TYPEИскать только файлы, соответствующие TYPE. Несколько типов флагов могут быть предоставлены.

-T/ --type-not TYPEНе искать файлы, соответствующие типу.

Используйте --type-listфлаг, чтобы перечислить все доступные типы.

Вот несколько простых примеров:

rg -Tjs "OK"                              # Excludes *.js, *.jsx, *.vue files.
rg -tpy "OK"                              # Includes Python files.
rg --type-add 'map:*.map' -tmap PATTERN   # Excludes *.map files.
rg -g '!*.js' -g '*.debug.js' PATTERN     # Excludes *.js apart of *.debug.js.

Вот полное решение исключить *.~, *.map, *.js, но не *.debug.js:

rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' "OK"

Тестирование:

$ touch file.~ file.map file.js file.debug.js file.txt file.md
$ rg --files
file.debug.js
file.js
file.map
file.md
file.txt
$ rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' --files
file.debug.js
file.md
file.txt
kenorb
источник