С терминалом: использование регулярных выражений

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

ВНИМАНИЕ: этот пост - заноза в заднице. Постоянное чтение этого поста может вызвать потерю сознания. Делайте перерывы между ними или проконсультируйтесь с врачом или фармацевтом, прежде чем читать всю публикацию.

Что такое регулярное выражение?

Регулярное выражение - это серия специальных символов, которые позволяют нам описать текст, который мы хотим найти. Например, если бы мы хотели найти слово «linux», было бы достаточно ввести это слово в используемую нами программу. Само слово является регулярным выражением. Пока это кажется очень простым, но что, если мы хотим найти все числа в определенном файле? Или все строки, начинающиеся с заглавной буквы? В таких случаях вы больше не можете использовать простое слово. Решение - использовать регулярное выражение.

Регулярные выражения и шаблоны файлов.

Прежде чем я перейду к регулярным выражениям, я хочу прояснить распространенное заблуждение о регулярных выражениях. Регулярное выражение - это не то, что мы указываем в качестве параметра в таких командах, как rm, cp и т. Д. Для обозначения различных файлов на жестком диске. Это будет шаблон файла. Регулярные выражения, хотя и похожи в том, что они используют некоторые общие символы, различны. Шаблон файла запускается для файлов на жестком диске и возвращает те, которые полностью соответствуют шаблону, в то время как регулярное выражение запускается для текста и возвращает строки, содержащие искомый текст. Например, регулярное выражение, соответствующее шаблону *.* это было бы что-то вроде ^.*\..*$

Типы регулярных выражений.

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

Прежде всего, существует два основных типа регулярных выражений, которые содержатся в стандарте POSIX, который используется инструментами Linux. Это базовые и расширенные регулярные выражения. Многие команды, работающие с регулярными выражениями, такие как grep или sed, позволяют использовать эти два типа. О них я расскажу ниже. Существуют также регулярные выражения в стиле PERL, а есть такие программы, как vim или emacs, которые используют их варианты. В зависимости от того, что мы хотим сделать, может быть более подходящим использовать одно или другое.

Тестирование регулярных выражений.

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

Первая - это команда grep. Это команда, которую мы будем использовать чаще всего для поиска. Синтаксис следующий:

grep [-E] 'REGEX' FICHERO
COMANDO | grep [-E] 'REGEX'

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

Уловка, которая может помочь нам увидеть, как работают регулярные выражения, - это включить использование цвета в команде grep. Таким образом, будет выделена часть текста, соответствующая используемому нами регулярному выражению. Чтобы активировать цвет в команде grep, просто убедитесь, что переменная среды GREP_OPTIONS содержать в ценности --color, что можно сделать с помощью этой команды:

GREP_OPTIONS=--color

Мы можем поместить его в .bashrc, чтобы он всегда был активен.

Другой способ использовать регулярные выражения - использовать команду sed. Это больше подходит для замены текста, но также может использоваться для поиска. Синтаксис для него будет таким:

sed -n[r] '/REGEX/p' FICHERO
COMANDO | sed -n[r] '/REGEX/p'

Команда sed также по умолчанию использует базовые регулярные выражения, вы можете использовать расширенные регулярные выражения с параметром -r.

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

awk '/REGEX/' FICHERO
COMANDO | awk '/REGEX/'

Эта команда всегда использует расширенные регулярные выражения.

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

- Lista de páginas wiki:

ArchLinux: https://wiki.archlinux.org/
Gentoo: https://wiki.gentoo.org/wiki/Main_Page
CentOS: http://wiki.centos.org/
Debian: https://wiki.debian.org/
Ubuntu: https://wiki.ubuntu.com/

- Fechas de lanzamiento:

Arch Linux: 11-03-2002
Gentoo: 31/03/2002
CentOs: 14-05-2004 03:32:38
Debian: 16/08/1993
Ubuntu: 20/10/2004

Desde Linux Rulez.

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

Начало урока.

Теперь у нас есть все необходимое для тестирования регулярных выражений. Пойдем мало-помалу. Я собираюсь привести несколько примеров поиска с регулярными выражениями, в которых я объясню, для чего нужен каждый символ. Это не очень хорошие примеры, но поскольку у меня будет очень длинный пост, я не хочу больше его усложнять. И я просто коснусь того, что можно сделать с помощью регулярных выражений.

Самый простой из всех - найти определенное слово, например, предположим, что мы хотим найти все строки, содержащие слово «Linux». Это самый простой способ, так как нам нужно только написать:

grep 'Linux' regex

И мы видим результат:

АркаLinux: https://wiki.archlinux.org/ Arch Linux: 11-03-2002 С Linux Рулез.

Это три строки, которые содержат слово «Linux», которое, если мы воспользуемся цветовым трюком, будет выделено. Обратите внимание, что он распознает слово, которое мы ищем, даже если оно является частью более длинного слова, как в «ArchLinux». Однако он не выделяет слово «linux», которое появляется в URL-адресе «https://wiki.archlinux.org/». Это потому, что он появляется там со строчной буквой «l», а мы искали его в верхнем регистре. У команды grep есть параметры для этого, но я не буду говорить о них в статье, посвященной регулярным выражениям.

С помощью этого простого теста мы можем сделать первый вывод:

  • Обычный символ, помещенный в регулярное выражение, соответствует самому себе.

То есть, если вы поставите букву «а», он будет искать букву «а». Это кажется логичным, правда? 🙂

Теперь предположим, что мы хотим найти слово «CentO», за которым следует любой символ, но только один символ. Для этого мы можем использовать символ «.», Который является подстановочным знаком, который соответствует любому символу, но только одному:

grep 'CentO.' regex

И вот результат:

CentOS: http://wiki.centos.org/
CentOS: 14-05-2004 03:32:38

Это означает, что он включает букву «S» в «CentOS», хотя в одном случае это заглавная буква, а в другом - строчная. Если в этом месте появится какой-либо другой персонаж, он также будет включен. У нас уже есть второе правило:

  • Персонаж "." соответствует любому символу.

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

grep '200[24]' regex

Это означает, что мы хотим найти число 200, за которым следует 2 или 4. И результат следующий:

АрхЛинукс: 11-03-2002
Gentoo: 31 /2002
Центров: 14-05-2004 03:32:38
Ubuntu: 20/10/2004

Это подводит нас к третьему правилу:

  • Несколько символов, заключенных в скобки, соответствуют любому из символов в скобках.

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

grep ':[^/]' regex

Просто поставьте «^» в качестве первого символа в скобках. Вы можете поместить всех желаемых персонажей ниже. Результат этой последней команды следующий:

ArchLinux: https://wiki.archlinux.org/
Gentoo: https://wiki.gentoo.org/wiki/Main_Page
CentOS: http://wiki.centos.org/
Debian: https://wiki.debian.org/
Ubuntu: https://wiki.ubuntu.com/
Arch Linux: 11 Gentoo: 31 CentOs: 14-05-2004 03:32:38 Debian: 16 Ubuntu: 20/10/2004

Теперь ":" за именами дистрибутивов выделяются, но не в URL-адресах, потому что URL-адреса имеют "/" после них.

  • Помещение символа «^» в начало скобки соответствует любому символу, кроме других символов в скобках.

Еще мы можем указать диапазон символов. Например, для поиска любого числа, за которым следует знак «-», это будет выглядеть так:

grep '[0-9]-' regex

При этом мы указываем символ от 0 до 9, а затем знак минус. Посмотрим на результат:

Арх Линукс: 11-03-Центров 2002 года: 14-05-2004 03:32:38

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

  • Размещение двух символов, разделенных знаком «-» в скобках, соответствует любому символу в пределах диапазона.

Теперь посмотрим, сможем ли мы выбрать первую часть URL-адресов. Тот, который говорит "http" или "https". Они различаются только конечными буквами "s", поэтому сделаем это следующим образом:

grep -E 'https?' regex

Знак вопроса используется для обозначения символа слева от него необязательным. Но теперь мы добавили в команду параметр -E. Это потому, что опрос - это функция расширенных регулярных выражений. До сих пор мы использовали базовые регулярные выражения, поэтому не нужно было ничего добавлять. Посмотрим на результат:

АрхЛинукс: протокол HTTPS: //wiki.archlinux.org/ Gentoo: протокол HTTPS: //wiki.gentoo.org/wiki/Main_Page CentOS: HTTP: //wiki.centos.org/ Debian: протокол HTTPS: //wiki.debian.org/ Ubuntu: протокол HTTPS: //wiki.ubuntu.com/

Итак, у нас уже есть новое правило:

  • Символ, за которым следует "?" соответствует этому символу или нет. Это действительно только для расширенных регулярных выражений.

Теперь мы найдем два совершенно разных слова. Давайте посмотрим, как найти строки, содержащие слово «Debian» и «Ubuntu».

grep -E 'Debian|Ubuntu' regex

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

Debian: https://wiki.debian.org/
Ubuntu: https://wiki.ubuntu.com/
Debian: 16 / 08 / 1993
Ubuntu: 20 / 10 / 2004
  • Символ "|" служит для разделения нескольких регулярных выражений и соответствует любому из них. Это также характерно для расширенных регулярных выражений.

Давай продолжим. Теперь мы будем искать слово «Linux», но только там, где оно не прикреплено к другому слову слева. Сделать это можно так:

grep '\

Здесь важным символом является «<», но его нужно экранировать, поставив перед ним «\», чтобы grep интерпретировал его как специальный символ. Результат такой:

Арка Linux: 11-03-2002 С Linux Рулез.

Вы также можете использовать "\>" для поиска слов, которые не расположены рядом друг с другом. Приведем пример. Попробуем эту команду:

grep 'http\>' regex

Результат такой:

CentOS: HTTP: //wiki.centos.org/

Вышел «Http», но не «https», потому что в «https» все еще есть символ справа от «p», который может быть частью слова.

  • Символы «<» и «>» соответствуют началу и концу слова соответственно. Эти символы должны быть экранированы, чтобы они не интерпретировались как буквальные символы.

Мы идем с вещами немного сложнее. Символ «+» соответствует символу слева от него, повторяется хотя бы один раз. Этот символ доступен только в расширенных регулярных выражениях. С его помощью мы можем искать, например, последовательности нескольких последовательных чисел, начинающихся с ":".

grep -E ':[0-9]+' regex

Результат:

CentOs: 14-05-2004 03: 32: 38

Число 38 также выделено, потому что оно также начинается с «:».

  • Символ «+» соответствует символу слева от него, повторяется хотя бы один раз.

Вы также можете контролировать количество повторений с помощью "{" и "}". Идея состоит в том, чтобы заключить в фигурные скобки число, которое указывает точное количество повторений, которое мы хотим. Также можно поставить диапазон. Давайте посмотрим на примеры двух случаев.

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

grep '[0-9]\{4\}' regex

Обратите внимание, что фигурные скобки должны быть экранированы, если мы используем базовые регулярные выражения, но не если мы используем расширенные. С расширенным это будет так:

grep -E '[0-9]{4}' regex

И результат в обоих случаях будет таким:

АрхЛинукс: 11-03-2002
Gentoo: 31 /2002
Центров: 14-05-2004 03:32:38
Debian: 16/08/1993
Ubuntu: 20/10 /2004
  • Символы "{" и "}" с числом между ними соответствуют предыдущему символу, повторяемому указанное количество раз.

Теперь другой пример с фигурными скобками. Предположим, мы хотим найти слова, содержащие от 3 до 6 строчных букв. Мы могли сделать следующее:

grep '[a-z]\{3,6\}' regex

И результат будет такой:

- Lишта de páginas Вики: TORCHLприток: протокол HTTPS:/ /Вики.Archlinux.org/ ГEntoo: протокол HTTPS:/ /Вики.папуасских.org/Вики/Mайн_Pвозраст
CЛОРОС: HTTP:/ /Вики.CentOS.org/ Dэбианский: протокол HTTPS:/ /Вики.Debian.org/ ИЛИ ЖЕбунт: протокол HTTPS:/ /Вики.Ubuntu.com/ - Fты скучаешь de запуск: TORCH Lприток: 11-03-2002 GEntoo: 31 CЛОРOs: 14-05-2004 03:32:38
Dэбианский: 16 Uбунт: 20 Dэто Lприток Rулез.

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

grep '\<[a-z]\{3,6\}\>' regex

Результат:

- Список страниц Вики: ArchLinux: протокол HTTPS:/ /Вики.архлинукс.org/ Gentoo: протокол HTTPS:/ /Вики.папуасских.org/Вики/ Main_Page CentOS: HTTP:/ /Вики.CentOS.org/ Debian: протокол HTTPS:/ /Вики.Debian.org/ Ubuntu: протокол HTTPS:/ /Вики.Ubuntu.com/

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

  • Символы "{" и "}" с двумя числами между ними, разделенными запятой, соответствуют предыдущему символу, повторяющемуся количество раз, указанное двумя числами.

Давайте теперь посмотрим на символ, который стоит перед знаком «+». Это «*», и его действие очень похоже, только оно соответствует любому количеству символов, включая ноль. То есть он делает то же самое, что и «+», но не требует, чтобы символ слева от него появлялся в тексте. Например, давайте попробуем найти те адреса, которые начинаются в вики и заканчиваются в организации:

grep 'wiki.*org' regex

Посмотрим на результат:

ArchLinux: https: //wiki.archlinux.org/ Gentoo: https: //wiki.gentoo.org/ wiki / Main_Page CentOS: http: //wiki.centos.org/ Debian: https: //wiki.debian.org/

Идеальный.

Теперь последний персонаж, которого мы увидим. Символ «\» используется для экранирования символа справа, так что он теряет свое особое значение. Например: предположим, мы хотим найти линии, заканчивающиеся точкой. Первое, что может прийти в голову, может быть следующее:

grep '.$' regex

Результат - не то, что мы ищем:

- Список вики-страниц:
АрхЛинукс: https://wiki.archlinux.org/
Gentoo: https://wiki.gentoo.org/wiki/Main_Pag.e
CentOS: http://wiki.centos.org./
Дебиан: https://wiki.debian.org/
Убунту: https://wiki.ubuntu.com/
- Даты выпуска: Arch Linux: 112
Gentoo: 312
CentOs: 14-05-2004 03:32:38
Debian: 163
Ubuntu: 204
Desde Linux Рулез.

Это потому, что "." он соответствует чему угодно, поэтому регулярное выражение соответствует последнему символу каждой строки, какой бы она ни была. Решение такое:

grep '\.$' regex

Результат - то, что мы хотим:

Desde Linux Рулез.

Game Over

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

А пока это все. Если вам понравилась эта статья, может, я напишу другую. А пока я рекомендую вам попробовать все регулярные выражения в терминале, чтобы понять, как они работают. И помните: только Чак Норрис может анализировать HTML с помощью регулярных выражений.


Оставьте свой комментарий

Ваш электронный адрес не будет опубликован. Обязательные для заполнения поля помечены *

*

*

  1. Ответственный за данные: Мигель Анхель Гатон
  2. Назначение данных: контроль спама, управление комментариями.
  3. Легитимация: ваше согласие
  4. Передача данных: данные не будут переданы третьим лицам, кроме как по закону.
  5. Хранение данных: база данных, размещенная в Occentus Networks (ЕС)
  6. Права: в любое время вы можете ограничить, восстановить и удалить свою информацию.

  1.   Иезекииль сказал

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

    1.    гексборг сказал

      Спасибо за комментарий. Я до сих пор не верю, что моя статья вышла. 🙂 Вышло с ошибкой, но я надеюсь, что это полезно. 🙂

  2.   Скалибур сказал

    Спасибо, ссссссс! ..

    Давным-давно мне пришлось немного изучить регулярные выражения .. Я благодарю вас за обучение .. и пошаговое руководство по изучению каждого из них ..

    Очень хорошо! .. Я собираюсь получить аспирин .. э-э

    1.    гексборг сказал

      Пожалуйста. Мужество и то, что регулярные выражения не могут с тобой. 🙂

  3.   Танракс сказал

    Фантастический пост! Прекрасная работа. Интересно, сколько часов у тебя ушло 😀

    1.    гексборг сказал

      СМЕШНО!! Возникает вопрос: сколько часов мне понадобилось бы, если бы я сказал все, что собирался сказать? Бесконечный !! 🙂

  4.   таммуз сказал

    одно я не знал, хорошая статья!

    1.    гексборг сказал

      Спасибо. Приятно поделиться с вами.

  5.   Елена_рюу сказал

    отличное объяснение. поздравляю! действительно полезно!

    1.    гексборг сказал

      Я рад, что ты нашел это полезным. Так что писать одно удовольствие.

  6.   анти сказал

    Это должно быть что-то особенное. Подобно избранному, но у него есть очень специфическая полезность. Довольно полезно, хотя я бы хотел, чтобы это применимо к Vim.

    1.    гексборг сказал

      Это вопрос, который задаю себе. У меня есть еще несколько статей о регулярных выражениях. И я мог бы говорить о vim в них. Он имеет некоторые отличия от того, что я объяснил в этой статье. Вопрос в том, как с этим справиться. 🙂

  7.   Фернандо сказал

    Хорошо!

    Ваша статья очень хорошая, любопытно, недавно (прямо сейчас) я опубликовал на своем сайте запись, которую готовил несколько дней, где собрал список метасимволов для регулярных выражений и несколько примеров. И было справедливо войти DesdeLinux и посмотрите запись на ту же тему!

    Если это утешает, то у меня НАМНОГО БОЛЬШЕ КИСКИ 😀

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

    Привет и очень хорошая работа.

    1.    гексборг сказал

      Большое спасибо.

      Мне тоже понравилась твоя статья. Он более лаконичен, чем мой. Он может служить кратким справочником. Это совпадение, что мы написали их одновременно. Вы можете видеть, что людям интересна тема. 🙂

  8.   Эллери сказал

    Регулярные выражения для чайников =), теперь мне стало понятнее, кстати, один из способов получить вывод с цветом для grep - это создать псевдоним в .bashrc alias grep = 'grep –color = always', если он у кого-то работает.

    привет

    1.    гексборг сказал

      Правда. Это еще один способ сделать это. Спасибо за вклад. 🙂

  9.   КЗКГ ^ Гаара сказал

    О_О… кусок вклада !!! О_О ...
    Большое спасибо за пост, я ждал чего-то вроде этого какое-то время, лол, я оставляю его открытым, чтобы спокойно прочитать его дома без каких-либо проблем, чтобы сосредоточиться, лол

    Спасибо за статью, правда

    1.    гексборг сказал

      Я знал, что тебе это понравится. СМЕШНО!! Правда в том, что многого не хватает, но у меня уже есть вторая часть. 🙂

  10.   Элиесер Татес сказал

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

    1.    гексборг сказал

      СМЕШНО!! Жаль, что я опоздал, но рад, что это помогло. 🙂

  11.   ЛеоТоро сказал

    Наконец !!!, супер хороший пост .... Наконец-то я нашел кое-что, что четко объясняет регулярные выражения ...

    1.    гексборг сказал

      Информации много, но труднее найти что-то, что легко понять. Я рад, что заполнил этот пробел. 🙂

      Привет.

  12.   Шекспир Родос сказал

    Привет, мне нужна помощь, мне нужно выполнить поиск в / var / logs в формате: yymmdd, а журналы выглядят так: 130901.log -130901.log, я должен искать все те, которые находятся в период с 1 сентября по 11 октября , Единственное, что мне удалось сделать, это удалить весь сентябрь, но я не знаю, как сделать всю цепочку:

    Пример: 1309 [0-3] возвращает мне журналы с 1 по 30 сентября, но я не знаю, как попасть в ту же цепочку с 1 по 11 октября.

    1.    гексборг сказал

      Сделать это с помощью регулярных выражений немного сложно. Мне приходит в голову, что что-то вроде этого может сработать:

      13(09[0-3]|10(0|1[01]))

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

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

      находить. -newermt '01 sep '-a! -newermt '11 oct '-print

      Удачи. Надеюсь, это поможет тебе.

  13.   чипо сказал

    Здравствуйте! Прежде всего, я хотел бы поблагодарить вас за вашу работу, так как эта страница входит в мою «тройку» лучших сайтов Linux.
    Я тренировался и не знал, почему у меня не работает регулярное выражение для телефонного номера, и мне не хватало «-E» (что я понял благодаря этой публикации).
    Я хотел спросить вас, если вы не знаете хорошего pdf-файла или сайта, где есть упражнения по RegExp, хотя, проявив немного воображения, вы можете попрактиковаться в их изобретении.

    Привет, Пабло.

  14.   Кэли сказал

    Очень хорошо, я только что все это прочитала, и да, теперь мне нужен аспирин 🙂

  15.   Оскар сказал

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

    Приветствие.

  16.   Александр сказал

    Мне очень понравилось очень хорошее объяснение