Метасимволы и квантификаторы.

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

«.» {точка} (любой символ).

Точка означает любой символ. Следует учесть, что символ всё-таки должен присутствовать, например RE /x.z/ будет найдено в строке «xyz», «x-z», «abcxRzdef», но не в строке «_xz_».

«[abc]» (символьный класс).

на этом месте может быть только «a», «b», или «c».

«[0-9]» (символьный диапазон).

Под диапазон попадают все символы диапазона, здесь 0,1,2,3,4,5,6,7,8,9

Следует учитывать, что почти все спец-символы теряют своё специальное значение внутри символьного класса. Следующие символы имеют специальное значение:

]
Завершение символьного класса.
-
Диапазон значений. Например [A-Z] совпадает с любой большой латинской буквой. Конечно этот класс совпадает и с малыми латинскими буквами, если вы используете регистронезависимые регулярные выражения (s/RE//i или /RE/I).

Предостережение

Следует учитывать, что диапазоны имеют смысл ТОЛЬКО для символов с кодами меньше 128и. Для символов в кодировке UTF-8 диапазоны вообще не имеют никакого смысла. В случае использования однобайтовых кодировок диапазоны имеют смысл, однако следует учитывать, что русские буквы вовсе не обязаны идти по порядку. Например /[а-я]/ не совпадает ни с какой русской буквой в кодировке KOI8-R. В кодировке cp1251 русские буквы совпадают с выражением /[а-яё]/, там все буквы идут вместе и по порядку, кроме буквы 'ё'. Конечно для обработки текста в кодировке cp1251 необходимо во первых писать sed-скрипт в этой-же кодировке, а во вторых настроить sed на нужную кодировку. (см. так-же)
См. так-же info sed.

Подсказка

Для включения в класс символа '-' его нужно расположить так, что-бы использование его как диапазона не имело смысла. Например [-0-9] совпадает с любой цифрой и минусом. Также это касается символа ']'.
\n, \t
Эти символы задают символ новой строки и табуляцию. При этом табуляция вообще-то непереносима. Лучше вместо табуляции задавать этот символ прямо.

Замечание

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

Многие хакеры используют символьные классы для записи спецсимволов, т.к. спецсимволы теряют своё значение внутри класса. Например данная команда удаляет из строки точки:

s/[.]//g

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

s~http://drbatty\.ru~http://drbatty.ru~

Здесь я заменил один адрес в сети (http://drbatty.ru) на другой.

[^xyz] (класс исключения).

вместо этого метасимвола будет найден любой символ кроме x или y или z

Замечание

Символ '^' является специальным только если он записан в самом начале класса. Например RE /[^^]/ совпадает с любым символом кроме '^'.

\s (Пробельные символы).

Здесь может быть пробел или табуляция, в много строчном режиме этот метасимвол так-же соответствует переводу строки. Кроме того, этот символ совпадает с символом возврат каретки ('\r').

\S (это любой не пробельный символ).

\w (словообразующий символ).

Эквивалентно [a-zA-Z0-9_]. Задаёт символы внутри слова Кроме того, в русской локали и в кодировке UTF-8 с \w совпадают так-же русские буквы. Конечно, при правильно настроенной локализации этот символ работает и в других кодировках. Пример.

\W (это не словообразующий символ).

\n (перевод строки).

в обычном режиме работы sed, этот символ никогда не встречается в строке(иначе это не одна строка, а 2)

\oXXX (Восьмеричное представление символа).

() (скобки) и обратные ссылки (\1, \2, \3 и т.д.)

Скобки в RE применяются для выделения подвыражений. Эти подвыражения применяются в следующих случаях:

  1. Если требуется повторить какое-либо подвыражение несколько раз, используя квантификаторы (см. ниже).
  2. Также возможна замена некоторого подвыражения, к примеру данная команда сотрёт из строки всё, кроме первого числа:
    s/[^0-9]*([0-9]+).*/\1/
    Тут sed просматривает строку, пропуская все не цифры до первой цифры. Началом найденного выражения считается позиция перед первой цифрой. Концом совпадения считается позиция после последней цифры в первом числе. Вся строка заменяется на найденное подвыражение - я использовал ссылку на него - '\1'. К примеру поиск подвыражения используется при выравнивании строк, там использована команда
    s/^(.{,40})\s/\1\n/
    которая отделяет символом '\n' несколько символов (не более 40) после которых идёт пробельный символ (\s).
  3. Обратные ссылки на подвыражения можно использовать не только в ВЫРАЖЕНИИ_ДЛЯ_ЗАМЕНЫ команды s, но и в РЕГУЛЯРНОМ_ВЫРАЖЕНИИ_ДЛЯ_ПОИСКА той-же команды. Это позволяет искать и находить вообще непонятно что. Например эта команда меняет местами две строки в буфере:
    s/(.*)\n(.*)/\2\1/
    Мы не знаем, что находится в этих строках, нам известно лишь то, что они разделены символом '\n'. Эта команда взята из этого примера.
  4. Кроме того, скобки применяются для отделения альтернатив (см. ниже).

Для ссылок на подвыражения используются обратные ссылки вида \N, где N представляет собой одну цифру из диапазона 1...9.

Замечание

Ссылка '\0' так-же допустима в GNU sed, она заменяется на всё найденное выражение, в точности так-же как и '&'. Однако в стандарте об этом не сказано, потому такую запись лучше не применять.

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

echo "ABCDEF---" | sed -r 's/(\w)+/\1/'
F---

Тут мы ищем буквы, которые повторяются как минимум 1 раз. Такое выражение захватывает «ABCDEF», и меняется на первое подвыражение. На самом деле, это первое подвыражение (\1) последовательно принимает значения A, B, C, D, E, и F, но заменяется найденная строка именно на последнее найденное совпадение («F» в данном случае).

«|» альтернатива

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

s/света|маша|даша|лена|катя|надя|таня/девочка/gi

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

s/Нин(а|ой|е)/девочк\1/

Заменит фразу «Выпить пивка с Ниной» на фразу «Выпить пивка с девочкой». Тут окончание сохраняется, и передаётся в заменяющее выражение.

Замечание

Русский язык - дьявольски сложная штука: мы с лёгкостью можем попить пивка со Светой, но при обработке этого текста нам придётся учесть, что если женское имя начинается на 'С', то нам придётся менять предлог 'со' на предлог 'c', так-как слово «девочка» начинается не на 'c'.

Если найдены обе альтернативы, то совпадением считается то, которое больше. Такое поведение связано с тем, что альтернативы ищутся параллельно, но большее совпадение затирает меньшее.

\ Экранирование.

любой спецсимвол можно за экранировать, если нам нужна к примеру именно точка, а не "любой символ". Существует 2 вида написания регэкспов, обычный, и расширенный(ERE). В дальнейшем я буду использовать только расширенный способ записи, так как он короче и понятнее. Что-бы sed работала в расширенном режиме необходима опция -r. ERE отличается от RE тем, что в ERE почти все спецсимволы не нужно экранировать слешем.

Пример 5.1. Обычное(RE):

/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/

Пример 5.2. Расширенное(ERE):

/([0-9]{1,3}\.){3}[0-9]{1,3}/

Большинство спец-символов в не экранированном виде имеют специальное значение, а в экранированном - обычное, однако есть и исключения (про исключения сказано отдельно для каждого спец-символа).

\xXX (шестнадцатеричное).

любой символ в шестнадцатеричной записи. Например /\x21/ то-же что /!/

«^» (начало).

Совпадает с началом строки - позиция перед первым символом (если он есть).

«$» (конец).

Совпадает с концом строки, позиция после последнего символа. Если последнего символа нет (а значит - вообще нет символов, строка пуста), то позиция «^» совпадает с «$».

«\<» (левая граница слова).

Этот метасимвол совпадает с позицией между не буквой слева и буквой справа. Кроме того, этот метасимвол совпадает с позицией между началом строки «^» и буквой. Этот метасимвол нужно экранировать даже в ERE, иначе он теряет специальное значение. Пример.

«\>» (правая граница).

Совпадает с правой границей слова. Следует учесть, что буквой считается \w, а не буквой - \W (или $), потому для русских букв всё будет работать только если правильно настроена локаль.

«\b» (граница слова).

Совпадает как с левой, так и с правой границей слова.

Замечание

Такое поведение типично только для регулярных выражений в sed. В bash символ «\b» имеет другое значение: это «пробел назад (backspace)» - использование этого символа в bash приводит к тому, что предыдущий символ затирается и на его месте печатается новый. Также этот символ называется «Забой».

Однако, что-бы использовать этот символ в bash-команде echo нужно применить опцию -e, как и при использовании других спец-символов, например '\n'.

Кроме того, вы можете использовать bash-команду printf, которая так-же понимает подобные спец-символы.

На самом деле, и echo, и printf являются встроенными встроенными командами bash'а, т.е. исполняются самим командным интерпретатором. Однако, в большинстве систем имеются и отдельные исполняемые файлы /usr/bin/printf и /usr/bin/echo. Их действие не отличается от встроенных команд. За исключением разве что времени обработки - встроенные команды работают конечно быстрее.

В ВЫРАЖЕНИИ_ДЛЯ_ЗАМЕНЫ sed-команды s символ «\b» не выполняет никаких действий, и заменяется на символ «b» . Я правда не в курсе, совпадает-ли такое поведение со стандартом. Однако для GNU sed версии 4.2 это так...

\` (начало буфера).

Этот метасимвол совпадает с началом буфера. Обычно он срабатывает так-же как ^, за исключением случая, когда одновременно выполняются 2 условия:
  1. В строке находятся несколько строк, т.е. имеются символы перевода строки \n. Они могут туда попасть после выполнения команд N, G, H, или после замены чего-либо командой s///. Но сами по себе эти символы никогда не загружаются в буфер из входного потока.
  2. Если включён многострочный режим (модификатором m для команды s///, либо модификтором M для адресного выражения).
Если выполнены оба условия, то ^ совпадает с началом каждой строки, а символ \` только с началом первой. Здесь я успешно применил многострочный режим, хотя мне и не понадобился метасимвол \`, но я зато успешно использовал свойства символа ^ совпадать сразу с несколькими местами в буфере - там у меня в буфере лежит целый конфигурационный файл из множества строк.

\' (конец буфера).

Работает в точности как и \`, однако совпадает с концом последней строки в многострочном режиме.

* (любое число повторений).

Этот символ является квантификатором, они ни с чем не совпадает, но задаёт число повторений предыдущего (мета)символа или даже целого подвыражения. Звёздочка допускает любое число повторений, в том числе и отсутствие (мета)символа. Как и остальные квантификаторы, звёздочка действует только на предыдущий (мета)символ, например, выражение /AB*/ совпадает с A, AB, ABB, ABBB, ABBBB и т.д..

+ (один или больше символов).

Так-же как и звёздочка, совпадает с любым числом символов, но в отличие от неё, хотя-бы один символ должен быть. Например с шаблоном /AB+/ не совпадает одиночный символ A.

? (может есть, а может и нет).

Задаёт необязательный (мета)символ (или необязательное подвыражение). К примеру целое число со знаком можно записать так: /[+-]?[0-9]+/.

{I,J} (повторение от I до J раз).

Этот квантификатор явно указывает сколько раз повторяется предыдущий (мета) символ, например /Z{3,5}/ совпадает только со строками ZZZ, ZZZZ, и ZZZZZ, и ни с чем иным. Можно опустить либо I, либо J, в первом случае I считается == 0, во втором - бесконечность.

Замечание

Как и остальные квантификаторы этот также жадный - он захватывает максимально возможное число совпадений.
Кроме того, можно точно указать число совпадений. к примеру выражение /(AB){3}/ совпадает только со строкой "ABABAB". Вот ещё пример: выражение совпадающее с MD5 суммой: /[[:xdigit:]]{32}/.

Подсказка

Этот квантификатор позволяет мне не использовать awk для работы с полями, к примеру, если я пожелаю вывести список файлов в формате размер имя, то мне будет достаточно взять пятый и восьмой столбцы:
ls -l | sed -r 's/(\S+\s+){4}(\S+\s+)(\S+\s+){2}(.*)/\2 \4/'

Вы можете обсудить этот документ на форуме. Текст предоставляется по лицензии GNU Free Documentation License (Перевод лицензии GFDL).

Вы можете пожертвовать небольшую сумму яндекс-денег на счёт 41001666004238 для оплаты хостинга, интернета, и прочего. Это конечно добровольно, однако это намного улучшит данный документ (у меня будет больше времени для его улучшения). На самом деле, проект часто находится на грани закрытия, ибо никаких денег никогда не приносил, и приносить не будет. Вы можете мне помочь. Спасибо.