Программы
Команда grep – полезные ключи и примеры использования

Команда grep – полезные ключи и примеры использования

У команды "grep" довольно много различных особенностей. особенно, если учесть, что с английского это "решето" – а что может войти в решето , не факт что выйдет!

Изображение Шпаргалка по командам Linux, FreeBSD и MacOS

Поиск слова в файле с помощью grep

Пожалуй, самый простой пример использования команды grep – это поиск подстроки в файле:

grep "Безухов" voyna-i-mir-tom-1.txt

– выведет строки, в которых grep нашёл точное совпадение. Однако, в этом списке строк сложно ориентироваться. Предлагаю немного апгрейдить нашу команду, чтобы и номера строк выводила, да ещё и контекст добавляла:

grep -in -C 3 "Безухов" voyna-i-mir-tom-1.txt

Пробежимся по ключам:

  • -i – игнорирование регистра – grep будет находить, даже если строчные буквы в подстроке оказались заглавными и наоборот. В случае с "Безуховым" это не так критично, но если бы мы искали "Граф", то в начале предложения оно таким и было бы, а вот внутри могло быть и с маленькой буквы.
  • -n – вывод номера строки, где было найдено вхождение нашей подстроки. Согласитесь, так приятнее и искать, да и представлять, на сколько подстрока разбросана по файлу, легче.
  • -C 3 – выводим по 3 строки до найденой строки и после – полезно понимать, в каком контексте была произнесена та или иная фраза. Для удобства каждая находка отделяется от других строкой из "--".

В итоге мы получим нечто следующее:

--
848-
849-Но дамы невольно смеялись и сами.
850-
851:— Насилу спасли этого несчастного, — продолжала гостья. — И это сын графа Кирилла Владимировича Безухова так умно забавляется! — прибавила она. — А говорили, что так хорошо воспитан и умен. Вот всё воспитание заграничное куда довело. Надеюсь, что здесь его никто не примет, несмотря на его богатство. Мне хотели его представить. Я решительно отказалась: у меня дочери.
852-
853-— Отчего вы говорите, что этот молодой человек так богат? — спросила графиня, нагибаясь от девиц, которые тотчас же сделали вид, что не слушают. — Ведь у него только незаконные дети. Кажется… и Пьер незаконный.
854-
--

Рекурсивный поиск текста в директории с grep

Искать в файле – это хорошо, но что делать, если ты не помнишь, в каком файле было то, что тебе нужно. К примеру, программируя на Си под MacOS я часто не могу понять, куда положили чудесные функции из сторонних библиотек. Тогда мне надо найти в директории /usr/local/include файл для подключения, имея только название функции:

$ grep -rl SDL_GetError /usr/local/include
/usr/local/include/SDL/SDL_error.h
/usr/local/include/SDL/SDL_ttf.h
/usr/local/include/SDL/SDL_mixer.h
/usr/local/include/SDL/SDL_image.h
/usr/local/include/SDL2/SDL_error.h
/usr/local/include/SDL2/SDL_main.h
/usr/local/include/SDL2/SDL_ttf.h
/usr/local/include/SDL2/SDL_mixer.h
/usr/local/include/SDL2/SDL_image.h

– значит SDL_GetError использовалась в перечисленных файлах – уже проще. Более того, по названию файлов я догадаюсь, что функция таки описана в /usr/local/include/SDL/SDL_error.h... Просто так привык. Давайте расскажу, что означают эти флаги, после расскажу, как поискать уже привычным нам образом: с выводом строк + контекстом, например, в 3 строки сверху и снизу. Кстати, вы уже можете задуматься сами – какие ключи/флаги надо установить grep. Итак,

  • -r – выполнять рекурсивно. А это значит, что мы, добавляя этот ключ, скармливаем grep не просто файл, а целую директорию (папку), в которой он будет искать по файлам.
  • -l – вывести список файлов. Просто, без дополнительной информации. Если вы хотите узнать только имена файлов, содержащих подстроку – вот ваш выбор. Также полезно для написания консольных утилит на bash.

Если же мы хотим спокойно и комфортно просмотреть все найденные вхождения, то нам скорее подойдёт:

$ grep -rin -C 3 SDL_GetError /usr/local/include
/usr/local/include/SDL/SDL_error.h-41- */
/usr/local/include/SDL/SDL_error.h-42-/*@{*/
/usr/local/include/SDL/SDL_error.h-43-extern DECLSPEC void SDLCALL SDL_SetError(const char *fmt, ...);
/usr/local/include/SDL/SDL_error.h:44:extern DECLSPEC char * SDLCALL SDL_GetError(void);
/usr/local/include/SDL/SDL_error.h-45-extern DECLSPEC void SDLCALL SDL_ClearError(void);
/usr/local/include/SDL/SDL_error.h-46-/*@}*/
/usr/local/include/SDL/SDL_error.h-47-
--
...

– все использованные флаги/ключи нам уже изветны. Если вам неочевидно, что тут происходит, перечитайте заметку выше.

Поиск по регулярному выражению в grep

Это всё, конечно, замечательно, но я бы хотел при поиске определения функции искать именно определение функции, без указания файлов, где она только упоминается. То есть мне нужна строка, где будет тип возвращаемого значения (char*), после которого будет идти какое-то количество спец-слов и пробелов, а потом название функции. Нет ничего проще:

grep -r -C 3 -E 'char.*?\*.*?SDL_GetError' /usr/local/include

Почти все ключи нам известны кроме -E – он включает режим расширенных регулярных выражений для шаблона поиска. Так в данном регулярном выраженнии мы написали 'char.?*.?SDL_GetError' то есть:

  • в строке должная встретиться подстрока "char",
  • сразу за ней должны встретиться любые символы до "*". Тут важно сказать, что . в регулярных выражениях отвечает за любой символ, * – модификатор, указывающий количество: от 0 до бесконечности повторений. Далее идёт ? – ограничитель жадности: да, любое количество повторений, пока не встретишь следующее из регулярного выражения.
  • \* – мы уже знаем, что * – спец. символ, означающий любое количество повторений. Поэтому, чтобы указать, что здесь должна быть просто *, мы используем экранирующий символ – \. Им же мы можем указать, что нужен символ "точка" или "вопросительный знак", не указывая на их дополнительные символы в рамках данного диалекта регулярных выражений.
  • снова .*? – съедаем все символы, не относящиеся к далее идущим в регулярке.
  • ну и наша функция "SDL_GetError".

Схематично это можно выразить следующим образом:

                | char | .*? | \* |  .*?    | SDL_GetError |
extern DECLSPEC | char |     | *  | SDLCALL | SDL_GetError | (void);

Регулярные выражения – отнюдь не очевидная тема. И понять её с ходу вы вряд лм сможете. Я подготовил некоторое объяснение в рамках второй пары по Linux. Но явно нужно более тщательное рассмотрение этой темы.