Утилита sed.

Программа grep выполняет только поиск строк и выводит найденные результаты без изменений. Однако часто бывает необходимо не только найти какой-либо текст, но и изменить его. Для редактирования потока текста можно использовать утилиту sed (от англ. Stream EDitor, потоковый редактор). sed используется для выполнения основных преобразований текста, читаемого из файла или поступающего из стандартного потока ввода, и совершает одно действие над вводом за проход. Общий формат вызова sed:

sed [options] COMMAND [FILE...]

Из большого числа возможных команд sed мы рассмотрим только команду поиска и замены текста. Эта команда имеет вид s/PATTERN/EXPRESSION/ и осуществляет поиск в каждой из входящих строк текста регулярного выражения PATTERN. Результаты совпадения заменяются на выражение EXPRESSION. Результирующий текст выводится в стандартный поток вывода.

Рассмотрим использование команды замены в sed на примерах.

В простейшем случае просто поменяем один фрагмент текста на другой:

$ ls -1 /var/cache

apt

fontconfig

man

$ ls /var/cache/ | sed 's/apt/APT/'

APT

fontconfig

man

В каталоге /var/cache есть несколько файлов, список их можно получить командной ls. Регулярное выражение «apt» совпадает с одной из строк вывода, и мы меняем совпадение на APT.

$ ls /var/cache/ | sed 's/a/A/'

Apt

fontconfig

mAn

В этом случае мы заменили в выводе ls букву a на А. sed применяет свои команды для каждой из строк вывода, поэтому в обеих строках, где была буква a, она была заменена.

Утилита uptime выдаёт определённую статистику по работе системы:

$ uptime

07:48:42 up 27 days, 22:13, 1 user, load average: 0.00, 0.00, 0.00

Для того, чтобы выделить из этой строки текущее число пользователей в системе, используем sed. Число пользователей — это одна или несколько цифр — «[0-9]\+», за которыми после пробела (или нескольких пробелов в общем случае) — «[0-9]\+ \+» следует слово user (или users). Нам интересно число пользователей — выберем его в подвыражении:
«\([0-9]\+\) \+user». В начале строки идёт некоторый текст, отделённый от числа пользователей пробелом: «^.* \([0-9]\+\) \+user». Конец строки тоже может быть любой: «^.* \([0-9]\+\) \+user.*».

Данное выражение совпадает со всей строкой и выделяет в подстроку \1 число пользователей. Заменив целиком строку на \1, мы получим в результате только это число:

$ uptime | sed 's/^.* \([0-9]\+\) \+user.*/\1/'

Аналогично можно получить, например, время работы системы (подстроку вида 27 days, 22:13):

$ uptime | sed 's/^.* up \+\(.\+\), \+[0-9]\+ \+user.*/\1/'

27 days, 22:13

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

• любое число любых символов от начала строки, далее пробел и слово up — ^.* up

• за которым следует через один или несколько пробелов время работы системы — ^.* up \+\(\)

• само время работы системы может содержать фактически любые символы, в т.ч. пробелы, знаки пунктуации и пр. —
^.* up \+\(.\+\)

• однако за ним через запятую и один или несколько пробелов —
^.* up \+\(.\+\), \+

• следует количество пользователей (число, одна или несколько
цифр) — ^.* up \+\(.\+\), \+[0-9]\+

• и слово user (или users). Далее до конца строки может быть что угодно — ^.* up \+\(.\+\), \+[0-9]\+ \+user.*

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

$ uptime | sed 's/user.*//'

08:18:07 up 27 days, 22:43, 2

убирает весь текст от user включительно и до конца строки. Также убираем в полученном результате и всё в конце строки от запятой включительно:

$ uptime | sed 's/user.*//'| sed 's/,[^,]*$//'

08:24:13 up 27 days, 22:49

Отметим, что более простой вариант без привязки к концу строки

$ uptime | sed 's/user.*//'| sed 's/,[^,]*//'

08:24:18 up 27 days, 2

из-за «ленивости» регулярных выражений совпадёт с первым вхождением запятой (, 22:43), а ещё более простой вариант

$ uptime | sed 's/user.*//'| sed 's/,.*$//'

08:25:11 up 27 days

из-за «жадности» будет совпадать с текстом от первой запятой до конца строки (, 22:43, 2).

Далее нам нужно удалить текст от начала строки до up включительно:

$ uptime | sed 's/user.*//'| sed 's/,[^,]*$//' | \

sed 's/^.*up \+//'

27 days, 22:54

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