Содержание

Общие понятия.
Первые sed-скрипты.
Регулярные выражения(общие понятия).
Расширенные регулярные выражения.
Простые примеры.
Любой символ «.».
Вопросительный знак «?».
Плюс (+) и его жадность.
Символьные классы.
Фигурные скобки {}.
Скобки ().
Якоря.
Границы.
Другие специальные символы.
Заключение.
Регулярные выражения изнутри.
Поиск символа.
Поиск шаблона из нескольких символов.
Замечание про кодировки.
Нечёткий поиск.
Повторения.
Поиск альтернатив.
Конкатенация.
Обратные ссылки.
Производительность и быстродействие sed.
Время выполнения sed-скриптов.
Время выполнения регулярных выражений в sed.
Оптимизация команды s.
Оптимизация команды x.
Оптимизация команд d и D.
BUGS (Ошибки в sed).
Вступление (Эта программа работает криво! Что делать?)
Команда N и последняя строка.
Синтаксис регулярных выражений (проблемы с обратным слешем).
sed -i портит файлы "только для чтения"!
Выражение /[А-Я]/ не совпадает с буквами русского алфавита!, а команда s/.*// не стирает буфер!
sed FAQ (ЧАВО)
Q. Почему меняется не все, а только первое совпадение в строке?
Q. Как инвертировать RE, например найти все строки, где НЕТ строки «ABC»?
Q. Почему выражение /.*XYZ/ работает не так, как мне хочется? (где тут не жадные выражения?)
Q. Почему не работает символьный класс [а-я]? Почему команда s/.*// не стирает все символы?
Q. Почему команда sed -i редактирует файлы "только для чтения"?
Q. Почему иногда срабатывает, а иногда не срабатывает команда перехода t?
Q. Как обработать несколько строк сразу?
Что нового в версии 4.2 по сравнению с 4.1.5?

Общие понятия.

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

Важно

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

Процедура 2.1. Обработка текста утилитой sed.

  1. Загрузка строки из потока.

    На этом этапе строка загружается в буфер. Буфером называется выделенная sed область памяти, размер которой не ограничен (для GNU версии sed, ну конечно на практике размер ограничен объёмом оперативной, и swap-памяти).

    Замечание

    Здесь и далее речь идёт только о GNU версии sed. В некоторых других версиях имеются ограничения.

    Загрузка заканчивается после чтения из потока символа новой строки (\n), или после завершения потока. При этом символ новой строки хотя и читается из потока, однако не пишется в буфер.

  2. Обработка строки.

    На этом этапе выполняется sed-скрипт, при этом содержимое буфера обычно изменяется. sed-скрипт состоит из особых sed-команд, каждая из которых представляет собой одну из букв латинского алфавита. Как обычно, малые и БОЛЬШИЕ буквы различаются: n и N это разные команды. Проще всего записывать sed-команды в командной строке, сразу после sed и её ключей, например:

    sed -n 'p;p;p'

    Для разделения команд используется точка с запятой (;).

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

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

    Подсказка

    Если внутри скрипта вам необходимо использовать символ одиночной кавычки, то вы можете воспользоваться её шестнадцатеричным представлением: «\x27».

    Команды sed могут изменить содержимое буфера, но кроме того, как и в других языках программирования, в sed-скриптах можно применять команды условных и безусловных переходов (b, t, и T), имеются также команды прерывания работы (q и Q). Некоторые команды воздействуют не только на этап обработки строки, но и на другие этапы, кроме того, внутри скрипта можно ввести ещё одну или несколько строк из входного потока (как на первом этапе).

    Важно

    Нужно понимать, что sed работает с потоком, а не с файлом, она просматривает строки только последовательно, с начала и до конца. Потому невозможно прочитать десятую строку после двадцатой - если в этом есть необходимость, следует сохранить десятую строку во время её обработки. Так-же невозможно узнать, что данная строка является например предпоследней, не смотря на то, что вполне можно определить номер строки от начала потока. Можно выяснить, что данная строка является последней, но только в момент её обработки.

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

    Номер строки
    Тогда команда выполнится только для той строки, чей номер указан
    Диапазон номеров строк
    Команда выполнится для всех строк из указанного диапазона(диапазон указывается через запятую, вместо второго числа допустимо указывать `$', этот символ обозначает последнюю строку.
    Регулярное выражение

    Команда выполнится только если в буфере найдётся данное RE.

    Замечание

    Здесь и далее, сокращение RE расшифровывается как "регулярное выражение".

    Комбинированный диапазон.

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

    Замечание

    Строки в sed нумеруются с единицы. Сначала производится поиск строки, в которой есть совпадение с первым адресом диапазона, для этой строки выполняется команда. Начиная со следующей строки производится поиск второго адреса диапазона. Однако, для первой строки такое поведение можно изменить: если записать не `1,RE', а `0,RE', то регулярное выражение будет проверятся так-же и в первой строке.

    Подсказка

    Если вы хотите, что-бы одно адресное выражение действовало сразу не несколько команд, тогда заключите эти команды в {фигурные скобки}.

    Замечание

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

    Кроме написания скрипта сразу после команды, вы можете записать его в файл, для выполнения такого файла можно использовать опцию -f, например:

    $ sed -f my_script.sed test_file.txt

    Эта команда выполнит sed-скрипт my_script.sed для файла test_file.txt. Кроме того, используя sha-bang

    #!/bin/sed -f

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

    $ ./my_script.sed test_file.txt

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

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

  3. Вывод буфера

    После завершения работы скрипта sed выводит содержимое буфера в выходной поток. Однако, это далеко не всегда необходимо, если вам это не нужно, воспользуйтесь опцией -n, которая блокирует вывод буфера. Кроме того, на этом же этапе происходит вывод и некоторой другой информации, если в скрипте выполнились команды a, c, и/или i. Эти команды тоже выводят информацию в выходной поток, но не во время исполнения, а в этом этапе. Существуют три команды (d, D и Q), которые так-же подавляют вывод буфера на этом этапе.

Первые sed-скрипты.

Для начала изучения sed нам потребуется какой-нибудь простой текст, например этот:

Пример 2.1. Текст используемый для проверки скриптов.

test.txt
В чём отличие Linux от Windows?

Идеологические отличия

Отличия огромны. Достаточно сказать, что Linux всё-таки надо рассматривать как один из
диалектов операционной системы UNIX, в то время как Windows является оригинальной
разработкой компании Microsoft и начиналась как надстройка над DOS. Таким образом всё что
появилось в Windows спустя годы после выхода в свет: многозадачность, сетевые протоколы и
т.д., в Linux присутствовало практически изначально. Многие вещи были просто портированны
(перенесены) в Linux из UNIX. Многие вещи, которые есть в Linux недоступны в Windows до сих
пор. Например, вы можете оценить многозадачность запустив в Windows и Linux копирование
файлов на дискету. В то время как в Windows до сих пор работать с чем-то ещё становится
просто невозможно, Linux ведёт себя так, как будто запись вообще не идёт - он не замечает
этот процесс. Если Windows изначально строилась как средство улучшенного взаимодействия с
пользователем и не может работать без графического интерфейса, Linux вполне может обходится
без привычных окон, мышек и всего остально антуража, тем самым экономя память и снижая
требования к ресурсам машины при использовании Linux в качестве сервера.

Windows имеет редакции Server (Сервер) и Workstation (Рабочая станция), но в Linux всё не
так. Устанавливая Linux вы получаете полный набор программного обеспечения и можете
использовать эту операционную систему и как сервер и как рабочую станцию. Вам
предоставляется полная свобода выбора, без всяких кодов и ключей и только от вас зависит как
вы будете использовать ваш Linux: хоть в качестве сервера на предприятии, где вы работает,
хоть дома в качестве рабочей машины или вообще на ноутбуке.


Такой вот коротенький текстик. Как обычно, примеры кодов и текстов я буду набирать моноширным шрифтом, что-бы все буквы были одинаковыми. Что-бы создать тестовый файл, сначала скопируйте в буфер этот текст, а затем в каком-нибудь эмуляторе терминала (в командной строке, но только не в той, что вызывается по CTRL+ALT+Fn) Наберите:

$ cat > test.txt

После чего, можно вставлять буфер, как? Зависит от терминала, обычно работает CTRL+INS, может сработает левая или средняя кнопка мыши, а может в меню есть нужный пункт, после вставки нажмите CTRL+D, для указания того, что файл завершён. Кроме того, можно воспользоваться gvim'ом, либо любым другим текстовым редактором.

Теперь можно написать первый sed-скрипт:

$ sed '' test.txt

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

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

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

Во-вторых, обратите внимание: сначала идёт скрипт, а после него идут файлы.

Теперь немного изменим наш "скрипт", заставим sed поменять все слова "Windows" на "Must Die", для этого нам понадобится sed-команда s, эта команда ищет в строке нужную одну строку, и заменяет её на другую:

$ sed 's/Windows/Must Die/' test.txt
В чём отличие Linux от Must Die?

Идеологические отличия

Отличия огромны. Достаточно сказать, что Linux всё-таки надо рассматривать как один из
диалектов операционной системы UNIX, в то время как Must Die является оригинальной
разработкой компании Microsoft и начиналась как надстройка над DOS. Таким образом всё что
появилось в Must Die спустя годы после выхода в свет: многозадачность, сетевые протоколы и
т.д., в Linux присутствовало практически изначально. Многие вещи были просто портированы
(перенесены) в Linux из UNIX. Многие вещи, которые есть в Linux недоступны в Must Die до сих
пор. Например, вы можете оценить многозадачность запустив в Must Die и Linux копирование
файлов на дискету. В то время как в Must Die до сих пор работать с чем-то ещё становится
просто невозможно, Linux ведёт себя так, как будто запись вообще не идёт - он не замечает
этот процесс. Если Must Die изначально строилась как средство улучшенного взаимодействия с
пользователем и не может работать без графического интерфейса, Linux вполне может обходится
без привычных окон, мышек и всего остально антуража, тем самым экономя память и снижая
требования к ресурсам машины при использовании Linux в качестве сервера.

Must Die имеет редакции Server (Сервер) и Workstation (Рабочая станция), но в Linux всё не
так. Устанавливая Linux вы получаете полный набор программного обеспечения и можете
использовать эту операционную систему и как сервер и как рабочую станцию. Вам
предоставляется полная свобода выбора, без всяких кодов и ключей и только от вас зависит как
вы будете использовать ваш Linux: хоть в качестве сервера на предприятии, где вы работает,
хоть дома в качестве рабочей машины или вообще на ноутбуке.

Ага, сработало!

Но не всё так радужно, если мы попробуем поменять "Linux" на "TRUE OS" нас ждёт разочарование:

Рисунок 2.1. Поиск 'Linux'

Поиск 'Linux'


Не все слова "Linux" заменяются, некоторые остаются без изменений. Дело в том, что sed по умолчанию заменяет только первое найденное слово... Исправить это просто: достаточно в конце команды s добавить модификатор g (команда - это не только буква s, но и то что идёт в слешах: слово для поиска и слово для замены).

sed 's/Linux/TRUE OS/g' test.txt

Модификатор g позволяет заменять все совпадения. Имеются цифровые модификаторы, которые позволяют заменить лишь некоторые вхождения:

echo "aaaaaaa" | sed 's/a/A/3'
aaAaaaa

Меняется только третья буква «a».

Цифровой модификатор можно комбинировать с модификатором g:

echo "aaaaaaa" | sed 's/a/A/3g'
aaAAAAA

Как видите, меняется третья, и все последующие буквы. Т.о. модификатор g заставляет sed продолжать замены до тех пор, пока строка не кончится; а цифровой модификатор говорит о том, с какого совпадения мы начнём замены. Не надо думать, что модификатор g сильно замедлит работу, sed в любом случае просматривает всю строку, даже если она уже сделала единственную замену (безусловно, на сами замены время тратится. Кроме того, уходит время на сдвиг хвоста, после заменяемой части, если длинна заменяемой и заменяющей части не совпадают. При массовых заменах хвост приходится двигать множество раз, что может сильно замедлить выполнение программы).

Кроме модификатора g так-же имеются модификатор i, который приведёт к тому, что sed не будет различать малые и большие буквы (в прошлом примере будет заменено слово lINUX, а так-же LiNuX, и все прочие); Многие команды sed дублируются в модификаторах. К примеру существует команда w для записи в файл. Так же есть и модификатор w с точно таким-же функционалом. Кроме того, имеются и другие модификаторы, которые мы рассмотрим несколько позже.

Регулярные выражения(общие понятия).

На самом деле, sed ищет вовсе не слова, а Регулярные выражения. Регулярным выражением называется строка, в которой некоторые символы имеют специальное значение. Если обычный символ просто ищется в строке, то спец-символ задаёт особые правила для поиска.

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

Я обычно использую не простые, а расширенные регулярные выражения(ERE), ИМХО они более наглядные. Для их использования необходимо использовать sed с ключом -r. На самом деле это одно и то-же, отличие только в форме записи. Примеры:

Обычное регулярное выражение:

/\(.\+\)\{3\}/

Расширенное выражение:

/(.+){3}/

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

Простые примеры.

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

$ echo "ABCDEEEEDG" | sed -r 's/D/z/'
ABCzEEEEDG

Чаще всего встречается регулярное выражение «.*», оно состоит из двух спец-символов:

«.» (точка)
Означает - какой угодно символ, лишь-бы был хоть какой-нибудь.
*
Означает - прошлый символ может повторятся любое количество раз, в том числе и вовсе отсутствовать.

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

$ echo "ABCDEEEEDG" | sed -r 's/.*/z/'
z

Вот более сложный пример со спец-символом:

echo "ABCDEEEEDG" | sed -r 's/DE*/z/'
ABCzDG

Регулярные выражения следует читать слева-направо, потому как именно так они и просматриваются командой sed. Это выражение означает: Найти букву D, за которой идёт буква E, которая повторяется любое число раз. Тут видно что во-первых, спец-символ «*» действует только на один обычный символ(на букву E в данном случае), и во-вторых, видна жадность звёздочки, она захватила не "любое" количество E, а всё что смогла. В sed все модификаторы ведут себя точно так-же, и нет никакой возможности изменить это поведение, это очень полезно для ускорения работы наших скриптов. На самом деле наша строка делится на четыре части:

"ABC"
которая не подходит под шаблон /DE*/
"DEEEE"
которая подходит под шаблон, причём подходит пятью способами: "D", "DE", "DEE", "DEEE", и "DEEEE".
"D"
, эта часть подходит одним способом.
"G"
это не подходит.

Находится только первое совпадение в строке, причём это совпадение берётся с максимально большим числом повторений (если спец-символ повторений допускает любое число совпадений). Конечно, модификатор `g' позволяет найти и заменить все совпадения.

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

Ещё раз повторю: «*» означает - любое число повторений. В том числе и ноль. Т.е. под регулярное выражение /E*/ попадают и пустые строки тоже, и любые строки, в которых есть пустые строки. К примеру строка "ABC" - в ней регулярное выражение /E*/ можно найти ЧЕТЫРЕ раза:

$ echo "ABC" | sed 's/E*/z/g'
zAzBzCz

Как видите, найдено ровно 4 совпадения, при этом, в данной строке вообще нет символа «E».

Всё потому-что, как я уже ранее писал - совпадение, это место между символами. В строке «ABC» имеется 3 символа, и четыре места между символами, именно на эти 4 места и меняется RE /E*/.

Подсказка

Обычно этого не нужно. Потому в большинстве случаев следует использовать квантификатор+, он находит то, что ожидается. Во многих примерах (в т.ч. даже и в info sed (см. ниже) часто всё равно используется звёздочка (*) вместо плюса (+) - это связано с ленью - используя обычные (не расширенные) регулярные выражения, нам придётся экранировать плюс, т.е. писать /E\+/, что намного сложнее чем /E*/.

Любой символ «.».

Вот следующий пример:

echo "ABCDEEEEDG" | sed -r 's/D./z/g'
ABCzEEEz

Тут ищутся все совпадения с шаблоном /D./, что означает: Буква D, после которой идёт один любой символ, здесь найдено 2 совпадения сразу: DE и DG.

Важно

обращаю ваше внимание, что "любой символ" должен существовать, например в нашей строке есть `/G/', но нет `/G./', дело в том, что после единственной G нет вообще никаких символов.

Как всегда, речь идёт о символах, а вовсе не о байтах, например "Ф" в кодировке UTF-8 занимает 2 байта, однако sed считает эту букву одним символом. Далее будут неоднократно упоминаться "несимволы", например «\xD1» в кодировке UTF-8, данный несимвол является первой половиной многих русских букв, и не совпадает (сам по себе) ни с одним регулярным выражением. К примеру RE .* совпадает с любой строкой символов, в том-числе и со строкой в которой нет символов, однако, даже это RE не совпадает со строкой, в которой есть хотя-бы один несимвол. Будьте внимательны!

Вопросительный знак «?».

Вопросительный знак означает 0 или одно повторение:
$ echo "ABCDEEEEDG" | sed -r 's/DE?/z/g'
ABCzEEEzG
найдено 2 совпадения: `DE' и `D'.

Замечание

Этот спец-символ (как и многие другие) надо экранировать слешем в обычных RE, и не нужно в ERE. Обратите внимание, я применил ключ -r для того, что-бы не экранировать '?'.

Плюс (+) и его жадность.

Плюс работает в точности как `*', только тут символ который повторяется должен встречаться как минимум 1 раз.
$ echo "ABCDEEEEDG" | sed -r 's/D*/z/g'
zAzBzCzEzEzEzEzGz

$ echo "ABCDEEEEDG" | sed -r 's/D+/z/g'
ABCzEEEEzG
Такое дело, шаблон `/D*/' меняется на z перед любой буквой "не D", а если буква - D, то она меняется на z. Т.о. например "A" у нас поменялось на "zA". Шаблон `/D+/' меняет на z только подряд идущие буквы D.

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

Многие неправильно понимают жадность: например в выражении

s/(.*)X.*/\1/

очень часто ошибаются, считая, что захватится из "abc_def_ghi_C" только "abc", основная ошибка: непонимания того, что все звёздочки(и прочие квантификаторы) жадные, и sed обрабатывает строки и выражения слева-направо. Потому данное выражение захватит всё до последней буквы X, а вовсе не до первой - первая звёздочка ТОЖЕ жадная.

Особенно опасны такие случаи:

$ echo "___aaa" | sed -r 's/(.*)(a+)/\1~/'
___aa~

Мы ясно видим «aaa» в конце строки, и ждём, что sed их заменит на «~». Но происходит совсем другое. Дело в том, что sed вовсе ничего не угадывает и не подбирает, всё проще: в данном случае sed ищет два выражения в скобках одновременно. Решение о том, где находится граница принимается в конце разбора. Точнее, после просмотра каждого символа граница сдвигается (новая граница затирает значение старой). По это причине результат работы будет следующий: оба выражения захватят столько символов, что-бы совпадение произошло. Однако, наши квантификаторы могут захватить и больше символов.

Т.к. просмотр идёт слева-направо, то первый (левый) квантификатор захватит максимально возможное число символов. Второй захватит минимально-необходимое. Что мы и наблюдаем в этом примере: первый захватил 5 символов, а второй только один. Потому-что это плюс, была-бы звёздочка, то ей символов не досталось-бы вообще (для звёздочки "пусто" тоже совпадение).

Жадности посвящена большая часть этой книги. Действительно, как хорошо было например в php, что-бы захватить <b>произвольный текст с тегам внутри</b> нам было достаточно использовать нежадную звёздочку. В sed таких нет, есть только жадная, а она захватывает весь текст, до последнего тега.

Кажется, что это серьёзный недостаток sed, но это не так: доказано, что для нежадных квантификаторов невозможен однопроходный алгоритм. В реальных скриптах практически невозможно предсказать, сколько-же проходов будет выполнятся, что приводит к тому, что скрипты отлично работающие при отладке, начинают тормозить и виснуть в реальных применениях. Мало того, злоумышленник может намертво повесить любой сайт, просто сформировав и отправив соответствующее сообщение/комментарий, такой, что-бы php скрипт совершил-бы огромное число проходов для каждого показа этого поста. (конечно, в реальной жизни такой быдлокод обычно всё-же отлавливается при тестировании. И тем не менее.).

Всё это невозможно в sed. Мало того, все ограничения жадности можно обойти, а часто и использовать жадность с пользой.

Символьные классы.

Можно задать метасимвол, который совпадает с несколькими символами, например:
/[0123456789]/
Совпадает с любой цифрой. Допустимо указывать диапазон, тот-же класс можно записать так:
/[0-9]/
Это так-же любая цифра. Кроме того, можно задавать инверсию, например любой символ кроме цифр
/[^0-9]/

Подсказка

Отрицание в символьном классе можно использовать для борьбы с жадностью: к примеру, для выдирания ссылки из HTML выражение /<a href="(.*)">/ не подходит. Для исправления ситуации точку внутри круглых скобок достаточно заменить на класс любой символ кроме кавычки: /<a href="([^"]*)">/ в силу того, что внутри ссылок кавычек не бывает.
Таким образом, десятичное целое число со знаком можно записать так:
/[-+]?[0-9]+/
теперь запишу это по-русски: символ плюс или минус, которого может и не быть, а затем идёт несколько (хотя-бы один) символ не меньший нуля, и не больший девяти.

Фигурные скобки {}.

Фигурные скобки служат для задания числа повторений, к примеру
/Q{7}/
ищет в строке 7 букв Q подряд. Можно задать диапазон, например
/Q{3,9}/
ищет от 3х до девяти букв Q, как и при применении остальных квантификаторов, захватывается максимально возможное число символов. Диапазон можно задать частично,
/Q{1,}/
эквивалентно
/Q+/

Скобки ().

Часть выражения можно заключить в скобки `(' `)', и тогда эта часть образует подвыражение. Подвыражения используются для ссылок, к примеру, команда
s/.*([-+]?[0-9]+).*/\1/
выделит из строки последнее целое десятичное число со знаком.

Замечание

это выражение содержит ошибку, какую? И что-же в действительности будет выделено?
Ссылки можно использовать так-же и для задания повторений, к примеру
s/(\S+\s+){3}//
сотрёт первые три поля (полем здесь считается последовательность из нескольких не пробельных символов, после которых идут несколько пробельных). Но самое важное применение скобок - поиск одинаковых подстрок, например, это выражение найдёт 2 одинаковых символа:
/(.).*\1/
А это выражение находит только одинаковые строки, разделённые символом `\n':
/^(.*)\n\1$/

Якоря.

символы `^' и `$' означаю начало и конец буфера sed соответственно. Напомню, позицией совпадения в sed считается позиция между символами, потому, $ это позиция между последним символом и концом строки. Если символов вообще нет, то $ совпадает с ^.

Имеются ещё два якоря: «\`» и «\'». Так же как и предыдущие, эти якоря задают начало и конец буфера sed. Различие между ними проявляется тогда, когда в буфере несколько строк. Мы рассмотрим их ниже.

Границы.

Кроме якорей, sed различает также границы между словом и не словом. У слова есть левая «\<» и правая «\>». Имеется также граница, совпадающая с любой из предыдущих границ «\b».

Другие специальные символы.

Многие экранированные символы в sed имеют специальное значение: например «\s» совпадает с любым пробельным символом (пробел, табуляция, перевод строки и возврат каретки). Любой непробельный символ записывается так: «\S». Имеется символ «\w» совпадающий с любой буквой и с «_». Это очень важный символ для русских - у нас применяются множество кодировок, и во многих символы разбросаны непонятным образом. Небуква записывается «\W». Можно также задать символ его числовым кодом, например «\x59» эквивалентно «Y».

Заключение.

В в заключение этой главы я хочу отметить, что написал я тут так мало вовсе не потому, что тема эта такая незначительная, нет! Просто про регулярные выражения и мною и без меня написано уже слишком много, повторять мне не хочется, в вашей ОС есть множество документации по RE, и её всегда можно найти даже без интернета, если у вас есть даже самый поганый выход в сеть, вы всегда сможете найти то-же самое ещё и по-русски! Регулярные выражения страшны и непонятны только первое время, уверяю вас, уже через недельку активного использования, вы не только станете их понимать, но и даже почувствуете красоту некоторых формул!

Удачи!

Чуть выше был пример с ошибкой. В чём-же заключается ошибка? Давайте попробуем:

$ echo "ABC-234XYZ+17sss" | sed -r 's/.*([-+]?[0-9]+).*/\1/'
7

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

/.*/

/[-+]?/

/[0-9]+/

/.*/

Совпадение (а значит и замена) будет тогда, и только тогда, когда будут найдены все четыре части. Причём каждая часть ищется независимо и параллельно одна от другой. Причём каждое выражение захватывает сколько сможет. Выражение, которое стоит вначале, захватывает больше всех. Вот и в нашем случае, /.*/ пожрало все символы, кроме одной семёрки - одну семёрку ей пришлось оставить, т.к. выражение /[0-9]+/ совпадает только лишь с одним символом(или с несколькими, но не с пустой строкой), и если-бы звёздочка пожрала-бы и семёрку, то совпадения не было-бы вообще.

А вот + (плюс) от 17и был успешно пожран первой звёздочкой, она может это себе позволить, по той причине, что выражение /[+-]?/ может довольствоваться ничем. Вот оно ничто и получило. А именно место между `1' и `7'.

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

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