10 практических примеров команд Grep для разработчиков
Команда grep используется для поиска шаблонов в файлах. В этом руководстве показаны некоторые наиболее распространенные примеры команд grep, которые будут особенно полезны разработчикам программного обеспечения.
Недавно я начал работать с Asciidoctor.js и над шаблонами Asciidoctor.js-pug и Asciidoctor. js-проект.
Не всегда легко сразу добиться успеха, когда вы впервые копаетесь в базе кода, содержащей несколько тысяч строк. Но моим секретным оружием, позволяющим разобраться с таким количеством строк кода, является инструмент grep
.
Я собираюсь поделиться с вами, как использовать команду grep в Linux, с примерами.
Реальные полезные примеры команд grep в Linux
Если вы посмотрите на man
, вы увидите короткое описание инструмента grep
: «печатать строки, соответствующие шаблону. ”
Однако не обманывайтесь таким скромным определением: grep
— один из самых полезных инструментов в наборе инструментов Unix, и существует бесчисленное множество случаев его использования при работе с текстовыми файлами.
Всегда лучше иметь примеры из реальной жизни, чтобы понять, как все работает. Итак, я буду использовать дерево исходного кода Asciidoctor.js, чтобы проиллюстрировать некоторые возможности grep
.
Вы можете скачать это дерево исходного кода с GitHub и, если хотите, можете даже проверить тот же набор изменений, который я использовал при написании этой статьи. Это гарантирует, что вы получите результаты, полностью идентичные описанным в остальной части этой статьи:
git clone https://github.com/asciidoctor/asciidoctor.js
cd asciidoctor.js
git checkout v1.5.6-rc.1
1. Найти все вхождения строки (базовое использование)
Asciidoctor.js поддерживает движок JavaScript Nashorn для платформы Java. Я не знаю Нэшорна, поэтому мог бы воспользоваться этой возможностью, чтобы узнать о нем больше, изучив части проекта, ссылающиеся на этот движок JavaScript.
Для начала я проверил, есть ли какие-либо настройки, связанные с Nashorn, в файле package.json
, описывающем зависимости проекта:
linux@handbook:~$ grep nashorn package.json
"test": "node npm/test/builder.js && node npm/test/unsupported-features.js && node npm/test/jasmine-browser.js && node npm/test/jasmine-browser-min.js && node npm/test/jasmine-node.js && node npm/test/jasmine-webpack.js && npm run test:karmaBrowserify && npm run test:karmaRequirejs && node npm/test/nashorn.js",
Да, видимо, были какие-то специфичные для Нашорна тесты. Итак, давайте исследуем это немного подробнее.
2. Поиск без учета регистра в наборе файлов.
Теперь я хочу поближе взглянуть на файлы из каталога ./npm/test/
, в которых явно упоминается Нэшорн.
Поиск без учета регистра (опция -i
), вероятно, здесь лучше, так как мне нужно найти обе ссылки на nashorn
и Nashorn
(или любые другие сочетание символов верхнего и нижнего регистра):
linux@handbook:~$ grep -i nashorn npm/test/*.js
npm/test/nashorn.js:const nashornModule = require('../module/nashorn');
npm/test/nashorn.js:log.task('Nashorn');
npm/test/nashorn.js:nashornModule.nashornRun('jdk1.8.0');
Действительно, нечувствительность к регистру здесь пригодилась. В противном случае я бы пропустил оператор require('../module/nashorn')
. Без сомнения, позже мне следует изучить этот файл более подробно.
3. Найдите все несовпадающие файлы.
Кстати, есть ли в каталоге npm/test/
какие-нибудь файлы, не относящиеся к Nashorm? Чтобы ответить на этот вопрос, мы можем использовать опцию grep «печатать несовпадающие файлы» (опция -L
):
sh$ grep -iL nashorn npm/test/*
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js
Обратите внимание, как с опцией -L
вывод grep
изменился и теперь отображает только имена файлов. Итак, ни один из файлов выше не содержит строки «nashorn» (независимо от регистра). Это не значит, что они как-то не связаны с этой технологией, но, по крайней мере, буквы «н-а-с-х-о-р-н» здесь нет.
4. Поиск шаблонов в скрытых файлах и рекурсивно в подкаталогах.
Последние две команды использовали шаблон оболочки для передачи списка файлов для проверки команде grep
.
Однако здесь есть некоторые ограничения: звездочка (*
) не будет соответствовать скрытым файлам. Он также не будет соответствовать файлам (в конечном итоге), содержащимся в подкаталогах.
Решением было бы объединить grep
с командой find вместо того, чтобы полагаться на шаблон оболочки:
# This is not efficient as it will spawn a new grep process for each file
linux@handbook:~$ find npm/test/ -type f -exec grep -iL nashorn \{} \;
# This may have issues with filenames containing space-like characters
linux@handbook:~$ grep -iL nashorn $(find npm/test/ -type f)
Как я уже упоминал в комментариях к блоку кода выше, у каждого решения есть недостатки.
Что касается имен файлов, содержащих пробельные символы, я позволю вам изучить параметр grep -z
, который в сочетании с параметром -print0
команды find
, может смягчить эту проблему. Не стесняйтесь использовать раздел комментариев в конце этой статьи, чтобы поделиться своими идеями по этой теме!
Тем не менее, лучшим решением было бы использовать «рекурсивный» вариант grep. С помощью этой опции вы указываете в командной строке корень вашего дерева поиска (начальный каталог) вместо явного списка имен файлов для проверки.
С опцией -r
grep будет искать все файлы в указанном каталоге, включая скрытые, а затем рекурсивно спустится в любой подкаталог:
linux@handbook:~$ grep -irL nashorn npm/test/npm/
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js
На самом деле, с этой опцией я также мог бы начать исследование на уровень выше, чтобы увидеть, что есть тесты, не относящиеся к npm, которые также нацелены на Nashorn:
linux@handbook:~$ grep -irL nashorn npm/
Я позволю вам проверить эту команду самостоятельно, чтобы увидеть ее результат; но в качестве подсказки могу сказать, что вы должны найти еще много подходящих файлов!
5. Фильтрация файлов по имени (с использованием регулярных выражений)
Итак, похоже, в этом проекте есть некоторые специфические тесты Насхорна. Поскольку Nashorn — это Java, можно было бы задать еще один вопрос: «есть ли в проекте какие-нибудь исходные файлы Java, в которых явно упоминается Nashorn?» .
В зависимости от используемой вами версии grep
есть как минимум два решения для ответа на этот вопрос.
Первый — использовать grep
, чтобы найти все файлы, содержащие шаблон «nashorn», а затем передать вывод этой первой команды второму экземпляру grep
, отфильтровывая не-Java. исходные файлы:
linux@handbook:~$ grep -ir nashorn ./ | grep "^[^:]*\.java"
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/basic.js"));
Первая половина команды теперь должна быть понятна. А как насчет части «^[\^:]*\\.java»?
Если вы не укажете опцию -F
, grep
предполагает, что шаблон поиска является регулярным выражением. Это означает, что помимо простых символов, которые будут соответствовать дословно, у вас есть доступ к набору метасимволов для описания более сложных шаблонов. Шаблон, который я использовал выше, будет соответствовать только:
^
начало строки[^:]*
, за которым следует последовательность любых символов, кроме двоеточия\.
, за которым следует точка (точка имеет особое значение в регулярном выражении, поэтому мне пришлось защитить ее обратной косой чертой, чтобы выразить свое желание буквальное совпадение)java
, за которым следуют четыре буквы «java. »
На практике, поскольку grep
будет использовать двоеточие для отделения имени файла от контекста, я оставляю в разделе имени файла только строки, содержащие .java
. Стоит отметить, что это будет соответствовать также именам файлов .javascript
. Это то, что я позволю попробовать решить самостоятельно, если хотите.
6. Фильтрация файлов по имени с помощью grep
Регулярные выражения чрезвычайно эффективны. Однако в данном конкретном случае это кажется излишним. Не говоря уже о том, что в приведенном выше решении мы тратим время на проверку всех файлов в поисках шаблона «нашорн» — большая часть результатов отбрасывается на втором этапе конвейера.
Если вы используете версию GNU grep
, что вполне вероятно, если вы используете Linux, у вас есть другое решение с опцией --include
. Это указывает grep
выполнять поиск только в файлах, имя которых соответствует заданному шаблону glob:
linux@handbook:~$ grep -ir nashorn ./ --include='*.java'
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/basic.js"));
7. Поиск слов
В проекте Asciidoctor.js интересно то, что это многоязычный проект. По своей сути Asciidoctor написан на Ruby, поэтому, чтобы его можно было использовать в мире JavaScript, его необходимо «транспилировать» с помощью Opal, компилятора исходного кода Ruby в JavaScript. Еще одна технология, о которой я раньше не знал.
Итак, изучив особенности Nashorn, я поставил перед собой задачу лучше понять API Opal. В качестве первого шага в этом поиске я просмотрел все упоминания глобального объекта Opal
в файлах JavaScript проекта. Это может появиться в аффектах (Opal =
), доступе к членам (Opal.
) или, возможно, даже в других контекстах. Регулярное выражение поможет. Однако, опять же, у grep
есть более легкое решение для решения этого распространенного случая. Используя параметр -w
, он будет соответствовать только словам, то есть шаблонам, которым предшествует и за которым следует символ, не являющийся словом. Символ, не являющийся словом, — это начало строки, конец строки или любой символ, который не является ни буквой, ни цифрой, ни подчеркиванием:
linux@handbook:~$ grep -irw --include='*.js' Opal .
...
8. раскраска вывода
Я не копировал вывод предыдущей команды, так как там много совпадений. Если результат такой плотный, вы можете добавить немного цвета, чтобы облегчить понимание. Если это еще не настроено в вашей системе по умолчанию, вы можете активировать эту функцию с помощью опции GNU --color
:
linux@handbook:~$ grep -irw --color=auto --include='*.js' Opal .
...
Вы должны получить тот же длинный результат, что и раньше, но на этот раз строка поиска должна появиться в цвете, если это еще не было.
9. Подсчет совпадающих строк или совпадающих файлов.
Я дважды упоминал, что вывод предыдущих команд был очень длинным. Как долго именно?
linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86
Это означает, что у нас есть всего 86 совпадающих строк во всех проверенных файлах. Однако сколько разных файлов совпадают? С помощью опции -l
вы можете ограничить вывод grep
соответствующих файлов вместо отображения соответствующих <линии. Итак, это простое изменение покажет, сколько файлов соответствует:
linux@handbook:~$ grep -irwl --include='*.js' Opal . | wc -l
20
Если это напоминает вам опцию -L
, неудивительно: поскольку она относительно распространена, строчные и прописные буквы используются для различения дополнительных опций. -l
отображает соответствующие имена файлов. -L
отображает несовпадающие имена файлов. В качестве другого примера я предлагаю вам проверить руководство по опциям -h
/-H
.
Давайте закроем скобки и вернемся к нашим результатам: 86 совпадающих строк. 20 совпадающих файлов. Однако как распределяются совпадающие строки в совпадающих файлах? Мы можем узнать это, используя опцию -c
команды grep
, которая будет подсчитывать количество совпадающих строк для каждого проверенного файла (включая файлы с нулевыми совпадениями):
linux@handbook:~$ grep -irwc --include='*.js' Opal .
...
Часто этот вывод требует некоторой постобработки, поскольку он отображает результаты в том порядке, в котором файлы были проверены, а также включает файлы без каких-либо совпадений — то, что обычно нас не интересует. Последнее довольно легко решить:
linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$'
Что касается упорядочивания вещей, вы можете добавить команду сортировки в конце конвейера:
linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$' | sort -t: -k2n
Я позволяю вам проверить руководство по команде sort
, чтобы узнать точное значение использованных мной параметров. Не забудьте поделиться своими выводами в разделе комментариев ниже!
10. Нахождение разницы между двумя совпадающими наборами
Если вы помните, несколько команд назад я искал слово «Опал. Однако если я поищу в том же наборе файлов все вхождения строки «Опал», я получу еще около двадцати ответов:
linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86
linux@handbook:~$ grep -ir --include='*.js' Opal . | wc -l
105
Было бы интересно найти разницу между этими двумя наборами. Итак, что же представляют собой строки, содержащие четыре буквы «опал» подряд, но где эти четыре буквы не образуют целое слово?
Ответить на этот вопрос не так-то просто. Потому что одна и та же строка может содержать оба слова Opal, а также какое-то более крупное слово, содержащее эти четыре буквы. Но в первом приближении вы можете использовать этот конвейер:
linux@handbook:~$ grep -ir --include='*.js' Opal . | grep -ivw Opal
./npm/examples.js: const opalBuilder = OpalBuilder.create();
./npm/examples.js: opalBuilder.appendPaths('build/asciidoctor/lib');
./npm/examples.js: opalBuilder.appendPaths('lib');
...
Очевидно, моей следующей остановкой будет исследование объекта opalBuilder
, но это будет в другой раз.
Последнее слово
Конечно, вы не поймете организацию проекта, а тем более архитектуру кода, просто введя пару команд grep
!
Однако я считаю, что эта команда необходима для определения эталонов и отправных точек при изучении новой базы кода.
Итак, я надеюсь, что эта статья помогла вам понять возможности команды grep
и вы добавите ее в свой набор инструментов. Без сомнения, вы не пожалеете об этом!