<div><img src="https://top-fwz1.mail.ru/counter?id=3548135;js=na" style="position:absolute;left:-9999px;" alt="Top.Mail.Ru" /></div>
Бесплатный перенос IT-инфраструктуры в облако

Использование Grep и регулярных выражений для поиска текстовых шаблонов в Linux

Миша Курушин
Миша Курушин
Технический писатель
17 января 2025 г.
19
16 минут чтения
Средний рейтинг статьи: 5

GREP (акроним от «global regular expression print») — одна из самых популярных утилит в операционных системах Linux.

С помощью нее можно не только выполнять поиск фраз (последовательности символов) во множестве файлов одновременно на основе регулярных выражений, но и фильтровать консольный вывод других команд, оставляя только необходимую информацию.

В этом руководстве будет рассмотрен поиск заданных выражений в наборе текстовых файлов различного содержания с помощью утилиты GREP.

Все показанные примеры запускались на облачном сервере Timeweb Cloud под управлением операционной системы Ubuntu версии 22.04.

cloud

Схема использования

Схема команды GREP имеет следующий вид:

grep [ОПЦИИ] [ВЫРАЖЕНИЕ] [ИСТОЧНИКИ]

В этом случае:

  • ОПЦИИ. Специальные параметры (флаги), которые активируют те или иные механики работы утилиты, связанные с поиском выражений и выводом результатов.
  • ВЫРАЖЕНИЕ. Регулярное выражение (или обычная строка), содержащее фразу (паттерн, шаблон, последовательность символов), которую необходимо найти.
  • ИСТОЧНИКИ. Путь к файлам, внутри которых будет выполнять поиск указанного выражения.

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

[КОМАНДА] | grep [ОПЦИИ] [ВЫРАЖЕНИЕ]

Таким образом:

  • КОМАНДА. Произвольная команда с собственным набором параметров, вывод которой необходимо отфильтровать.

При этом символ «|» необходим для образования конвейера (pipeline) выполнения команд, который перенаправляет потоки так, что вывод произвольной команды становится вводом команды GREP.

Подготовка

Понять нюансы работы утилиты GREP лучше всего на небольших примерах по поиску вхождений заданных фраз. Поэтому сперва мы создадим несколько текстовых файлов, а после протестируем на них команду GREP.

Для начала подготовим отдельный каталог, в котором будет выполняться поиск:

mkdir /texts

После чего создадим первый файл:

nano texts/blok

Содержимым выступит одно из стихотворений Александра Блока:

Ночь, улица, фонарь, аптека,
Бессмысленный и тусклый свет.
Живи еще хоть четверть века —
Всё будет так. Исхода нет.
Умрёшь — начнёшь опять сначала
И повторится все, как встарь:
Ночь, ледяная рябь канала,
Аптека, улица, фонарь.

Теперь создадим второй файл:

nano texts/code.py

Его содержимым станет простой скрипт на языке Python:

from datetime import date

dateNow = date.today()
print("Текущее время:", dateNow)

Наконец, создадим третий файл:

nano texts/page.html

На этот раз внутри окажется тривиальная HTML-разметка:

<html>
	<head>
		<title>Некий заголовок</title>
	</head>

	<body>
		<div class="block">
			<p>Здесь есть золото</p>
		</div>

		<div class="block">
			<p>Смесь воска и облаков</p>
		</div>

		<div class="block block_special">
			<p>Днесь нет ничего</p>
		</div>
	</body>
</html>

Таким образом с помощью файлов разного формата можно более наглядно разобраться, что делает команда GREP, задействовав всю вариативность функций утилиты.

Регулярные выражения

Регулярные выражения — основа команды GREP. В отличие от обычной строки, регулярные выражения содержат специальные символы, позволяющие задавать фразы с определенной степенью изменчивости.

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

'^date[[:alpha:]]*'

Таким образом, полная команда может выглядеть так:

grep '^date[[:alpha:]]*' texts/*

Консольный вывод в этом случае будет таким:

texts/code.py:dateNow = date.today()

Однако использование двойных кавычек позволяет передавать в выражение различные системные данные. Например, можно сперва создать переменную окружения с искомым выражением:

PATTERN="^date[[:alpha:]]*"

А потом использовать ее в команде GREP: 

grep "$PATTERN" ./texts/*

В дополнение к этому, задействование одинарных косых кавычек (клавиша «Ё» в английской раскладке) позволяет использовать команды подпроцессора bash внутри команды GREP.

Например, можно извлечь регулярное выражение из заранее подготовленного файла:

grep `cat somefile` ./texts/*

Обратите внимание, что с помощью символа звездочки (маска) можно указать сразу все файлы, находящиеся в каталоге. Тем не менее, команда GREP не запрещает указание только одного файла:

grep '^date[[:alpha:]]*' texts/code.py

По той причине, что регулярные выражения являются универсальным языком, используемым во многих операционных системах и языках программирования, их изучение — отдельная обширная тема.

Тем не менее, имеет смысл коротко пройтись по основным специальным символам и их функциям.

При этом важно помнить, что регулярные выражения в Linux могут работать в двух режимах — базовом (Basic Regular Expression, BRE) и расширенном (Extended Regular Expression, ERE).

Расширенный режим активируется с помощью дополнительного флага -E. Разница между обоими режимами в количестве доступных спецсимволов и соответственно широте доступного функционала.

Базовый синтаксис

Базовый синтаксис позволяет задавать лишь общие формальные конструкции, не учитывающие специфику конфигурации их символов.

Начало строки — ^

Символ циркумфлекса указывает, что искомая последовательность символов должна находиться в начале строки:

grep '^Ночь' texts/*

Консольный вывод будет следующим:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:Ночь, ледяная рябь канала,

Конец строки — $

Знак доллара указывает, что искомая последовательность символов должна находиться в конце строки:

grep '</p>$' texts/*

В консольном терминале появится вот такой вывод:

texts/page.html:			<p>Здесь есть золото</p>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Обратите внимание, что консольный вывод сохраняет оригинальную запись найденных строк так, как они представлены внутри файлов.

Начало слова — \<

Символы обратной косой черты и знака меньше указывают, что искомая фраза должна находиться в начале слова:

grep '\<фо' texts/*

Итогом выполнения этой команды станет следующий консольный вывод:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:Аптека, улица, фонарь.

Конец слова — \>

Символы обратной косой черты и знака больше указывают, что искомая последовательность символов должна находиться в конце слова:

grep 'рь\>' texts/*

Консольный вывод после выполнения команды будет таким:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:И повторится все, как встарь:
texts/block:Аптека, улица, фонарь.

Начало или конец слова — \b

Указать начало или конец слова можно с помощью другой более универсальной последовательности символов — обратная косой черты и буквы b.

Например, так указывается начало:

grep '\bнет' texts/*

Консольный вывод будет таким:

texts/block:Всё будет так. Исхода нет.
texts/page.html:			<p>Днесь нет ничего</p>

А так конец:

grep '<div\b' texts/*

В этом случае вывод в консольном терминале будет следующим:

texts/page.html:		<div class="block">
texts/page.html:		<div class="block">
texts/page.html:		<div class="block block_special">

Либо можно указать и то, и другое:

grep '\bи\b' texts/*

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

texts/block:Бессмысленный и тусклый свет.
texts/page.html:                        <p>Смесь воска и облаков</p>

Произвольный символ — .

Некоторые символы в искомых фразах можно оставить без точного указания с помощью символа точки:

grep '..есь' texts/*

Вывод в терминале консоли окажется таким:

texts/page.html:			<p>Здесь есть золото</p>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Расширенный синтаксис

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

Объединение шаблонов — |

Чтобы не выполнять команду GREP несколько раз подряд, можно указать сразу несколько шаблонов в одном регулярном выражении:

grep -E '^Ночь|</p>$' texts/*

Результатом работы этой команды будет комбинированный консольный вывод, содержащий результат поиска двух отдельных регулярных выражений, показанных ранее:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:Ночь, ледяная рябь канала,
texts/page.html:			<p>Здесь есть золото</p>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Интервал повторений — {n, d}

В некоторых случаях определенные символы искомой фразы могут количественно отличаться. Поэтому в регулярном выражении можно указать диапазон допустимого количества определенных символов.

grep -E 'ес{1,2}' texts/*

Консольный вывод этого вызова будет таким:

texts/block:Бессмысленный и тусклый свет.
texts/page.html:			<p>Здесь есть золото</p>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Однако часто используемые интервалы повторений удобнее записывать в виде специальных символов, тем самым упрощая внешний вид регулярного выражения.

Одно или более повторений — +

Интервал повторений от одного до бесконечности можно выразить в виде знака сложения:

grep -E 'ес+' texts/*

В этом случае консольный вывод не будет отличаться от предыдущего примера:

texts/block:Бессмысленный и тусклый свет.
texts/page.html:			<p>Здесь есть золото</p>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Ноль или одно повторение — ?

Интервал повторений от 0 до 1 можно выразить в виде знака вопроса:

grep -E 'ss?' texts/*

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

texts/page.html:		<div class="block">
texts/page.html:		<div class="block">
texts/page.html:		<div class="block block_special">

Набор символов — [abc]

Вместо одного конкретного символа можно указать целый набор, обрамленный квадратными скобками:

grep -E '[Вв]с[её]' texts/*

Консольный вывод в этом случае будет таким:

texts/block:Всё будет так. Исхода нет.
texts/block:И повторится все, как встарь:

Диапазон символов — [a-z]

Слишком большой набор допустимых символов можно заменить на диапазон, записанный через тире:

grep -E 'h[a-z]+' texts/*

Результатом выполнения этой команды будет следующий вывод:

texts/page.html:<html>
texts/page.html:	<head>
texts/page.html:	</head>
texts/page.html:</html>

Более того, наборы и диапазоны символов можно объединять:

grep -E 'h[abcd-z]+' texts/*

Каждый диапазон неявно преобразуется в набор символов:

  • [a-e] в [abcde]
  • [0-6] в [0123456]
  • [a-eA-F] в [abcdeABCDEF]
  • [A-Fa-e] в [ABCDEFabcde]
  • [A-Fa-e0-9] в [ABCDEFabcde0123456789]
  • [a-dA-CE-G] в [abcdABCEFG]
  • [acegi-l5-9] в [acegijkl56789]

Тип символов — [:alpha:]

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

[:lower:]

символы от a до z в нижнем регистре

[:upper:]

символы от A до Z в верхнем регистре

[:alpha:]

символы всех букв

[:digit:]

символы всех цифр

[:alnum:]

символы всех букв и цифр

При этом важно понимать, что тип символов — отдельная синтаксическая конструкция. Это значит, что она сама по себе должна быть обрамлена квадратными скобками, обозначающими набор или диапазон символов:

grep -E '[[:alpha:]]+есь' texts/*

Консольный вывод этой команды будет как показано ниже:

texts/page.html:			<p>Здесь есть золото</p>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Фильтрация вывода

Чтобы отфильтровать вывод другой команды, после нее необходимо написать символ прямой черты, за которым следует стандартный вызов утилиты GREP, но без указания файлов для поиска:

cat texts/code.py | grep 'import'

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

from datetime import date

В данном случае команда cat извлекает содержимое файла и передает его в поток ввода утилиты GREP.

Опции поиска

Помимо регулярных выражений можно указать дополнительные ключи команды GREP — специальные опции в формате флагов, уточняющие поиск.

Расширенные регулярные выражения (-E)

Активирует режим расширенных регулярных выражений, разрешающий использовать большее количество специальных символов.

Игнорирование регистра (-i)

Выполняет поиск регулярного выражения, не учитывая регистр символов:

grep -E -i 'вс[её]' texts/*

Вот такой консольный вывод соответствует этой команде:

texts/block:Всё будет так. Исхода нет.
texts/block:И повторится все, как встарь:

При этом флаги можно указывать слитно:

grep -Ei 'вс[её]' texts/*

Целое слово (-w)

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

grep -w и texts/*

Обратите внимание, что при указании обычной строки, не содержащий специальные символы, кавычки можно не указывать.

Результатом выполнения этой команды станет вот такой консольный вывод:

texts/block:Бессмысленный и тусклый свет.
texts/page.html:			<p>Смесь воска и облаков</p>

Инвертированный поиск (-v)

По умолчанию команда GREP выводит в консольный терминал строки с найденными фразами. Однако результат можно инвертировать, выводя в консольный терминал строки без найденных фраз:

grep -v 'today' script.py

Консольный вывод будет содержать все строки кроме той, в которой было найдено вхождение:

from datetime import date

print("Текущее время:", dateNow)

Несколько выражений (-e)

Чтобы не вызывать команду несколько раз, можно сразу указать несколько выражений:

grep -e 'Ночь' -e 'html' texts/*

Результат работы этой команды будет идентичен такой записи:

grep -E 'Ночь|html' texts/*

В обоих случаях в консольном терминале появится следующий вывод:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:Ночь, ледяная рябь канала,
texts/page.html:<html>
texts/page.html:</html>

Рекурсивный поиск (-r)

Выполняет рекурсивный поиск в указанной директории до максимальной глубины вложенности: 

grep -r '[Ff]ilesystem' /root

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

/root/parser/parser/settings.py:#HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"
/root/resize.log:Resizing the filesystem on /dev/vda1 to 3931904 (4k) blocks.
/root/resize.log:The filesystem on /dev/vda1 is now 3931904 (4k) blocks long.

Поиск специальных символов (-F)

Разрешает использование специальных символов в качестве символов искомой фразы:

grep -F '[' texts/*

Без указания этого флага в консольном терминале возникнет ошибка:

grep: Invalid regular expression

Альтернативой этому флагу будет использование символа экранирования в виде обратной косой черты (\):

grep '\[' texts/*

Включение файлов (--include)

Позволяет ограничить поиск только указанными файлами:

grep --include='*.py' 'date' texts/*

Консольный вывод будет таким:

texts/code.py:from datetime import date
texts/code.py:dateNow = date.today()
texts/code.py:print("Текущее время:", dateNow)

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

grep -r --include='*.py' 'date' texts

Исключение файлов (--exclude)

Избирательно исключает определенные файлы из списка источников поиска:

grep --exclude='*.py' 'и' texts/*

Консольный вывод окажется следующим:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:Бессмысленный и тусклый свет.
texts/block:Живи еще хоть четверть века —
texts/block:И повторится все, как встарь:
texts/block:Аптека, улица, фонарь.
texts/page.html:		<title>Некий заголовок</title>
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html:			<p>Днесь нет ничего</p>

Опции вывода

Некоторые параметры команды GREP влияют только на вывод результатов поиска, улучшая его информативность и наглядность.

Номера строк (-n)

Для повышения информативности результата работы утилиты GREP можно добавить номера строк, в которых были найдены искомые фразы:

grep -n '</p>$' texts/*

Каждая строка в выводе будет дополнена номером строки

texts/page.html:8:			<p>Здесь есть золото</p>
texts/page.html:12:			<p>Смесь воска и облаков</p>
texts/page.html:16:			<p>Днесь нет ничего</p>

Строки до (-B)

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

grep -B3 'Смесь' texts/*

После флага указывается число предыдущих строк, которые требуется вывести в консольный терминал:

texts/page.html-		</div>
texts/page.html-
texts/page.html-		<div class="block">
texts/page.html:			<p>Смесь воска и облаков</p>

Строки после (-A)

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

grep -A3 'Смесь' texts/*

После флака указывается число последующих строк, которые требуется вывести в консольный терминал:

texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html-		</div>
texts/page.html-
texts/page.html-		<div class="block block_special">

Строки до и после (-C)

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

grep -C3 'Смесь' texts/*

После флака указывается число предыдущих и последующих строк, которые требуется вывести в консольный терминал:

texts/page.html-		</div>
texts/page.html-
texts/page.html-		<div class="block">
texts/page.html:			<p>Смесь воска и облаков</p>
texts/page.html-		</div>
texts/page.html-
texts/page.html-		<div class="block block_special">

Количество строк (-c)

Вместо списка найденных строк команды GREP выводит только их количество:

grep -c 'и' texts/*

Консольный вывод будет содержать количество найденных совпадений во всех указанных файлах:

texts/block:5
texts/code.py:0
texts/page.html:3

Если же в качестве источника указывается лишь один файл:

grep -c 'и' texts/block

То консольный вывод будет содержать только число:

5

Имена файлов (-l)

Флаг позволяет вывести только названия файлов, в которых найдены совпадения:

grep -l 'и' texts/*

В этом случае консольный вывод будет таким:

texts/block
texts/page.html

Ограничение вывода (-m)

Сокращает количество выводящихся в консольный терминал строк до указанного рядом с флагом:

grep -m2 'и' texts/*

Консольный вывод будет таким:

texts/block:Ночь, улица, фонарь, аптека,
texts/block:Бессмысленный и тусклый свет.
texts/page.html:		<title>Некий заголовок</title>
texts/page.html:			<p>Смесь воска и облаков</p>

Как видно, ограничивающее число влияет не на весь вывод в целом, а на строки каждого файла.

Поиск целой строки (-x)

Ищет точное совпадение целой строки без какой-либо вариативности:

grep -x 'Всё будет так. Исхода нет.' texts/*

Консольный вывод следующий:

texts/block:Всё будет так. Исхода нет.
Подготовили для вас выгодные тарифы на облачные серверы

Заключение

Команда GREP в Linux — наиболее гибкий и точный инструмент для поиска выражений в больших объемах текстовой информации.

Во время использования команды необходимо указать следующие элементы:

  • Специфический набор опций (флагов), настраивающий механизм поиска и вывода результатов.
  • Регулярное выражение (одно или несколько), описывающее искомую фразу.
  • Список источников (файлы и каталоги), в которых будет выполняться поиск.

Помимо этого утилита используется для фильтрации вывода других команд с помощью перенаправления потоков ввода и вывода.

При этом центральной частью команды GREP являются регулярные выражения. В отличие от обычной строки, они позволяют задать фразу с определенной степенью изменчивости так, чтобы она соответствовала нескольким похожим вхождениям.

Существует два режима работы регулярных выражений:

  • Базовый. Ограниченный набор специальных символов, позволяющий формализовывать выражения лишь в общих чертах.
  • Расширенный. Полноценный набор специальных символов, позволяющий формализовывать выражения с точностью до каждого символа.

Расширенный режим предоставляет полную гибкость и точность при работе с регулярными выражениями.

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

Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
17 января 2025 г.
19
16 минут чтения
Средний рейтинг статьи: 5
Пока нет комментариев