У команды "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-
--
Искать в файле – это хорошо, но что делать, если ты не помнишь, в каком
файле было то, что тебе нужно. К примеру, программируя на Си под 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-
--
...
– все использованные флаги/ключи нам уже изветны. Если вам неочевидно, что тут происходит, перечитайте заметку выше.
Это всё, конечно, замечательно, но я бы хотел при поиске определения функции искать именно определение функции, без указания файлов, где она только упоминается. То есть мне нужна строка, где будет тип возвращаемого значения (char*), после которого будет идти какое-то количество спец-слов и пробелов, а потом название функции. Нет ничего проще:
grep -r -C 3 -E 'char.*?\*.*?SDL_GetError' /usr/local/include
Почти все ключи нам известны кроме -E
– он включает режим расширенных
регулярных выражений для шаблона поиска. Так в данном регулярном выраженнии
мы написали 'char.?*.?SDL_GetError' то есть:
.
в регулярных выражениях отвечает за любой символ, *
– модификатор, указывающий
количество: от 0 до бесконечности повторений. Далее идёт ?
– ограничитель жадности:
да, любое количество повторений, пока не встретишь следующее из регулярного выражения.\*
– мы уже знаем, что *
– спец. символ, означающий любое количество повторений.
Поэтому, чтобы указать, что здесь должна быть просто *
, мы используем экранирующий
символ – \
. Им же мы можем указать, что нужен символ "точка" или "вопросительный знак",
не указывая на их дополнительные символы в рамках данного диалекта регулярных выражений..*?
– съедаем все символы, не относящиеся к далее идущим в регулярке.Схематично это можно выразить следующим образом:
| char | .*? | \* | .*? | SDL_GetError |
extern DECLSPEC | char | | * | SDLCALL | SDL_GetError | (void);
Регулярные выражения – отнюдь не очевидная тема. И понять её с ходу вы вряд лм сможете. Я подготовил некоторое объяснение в рамках второй пары по Linux. Но явно нужно более тщательное рассмотрение этой темы.