Примеры использования редактора sed

Avatar of admin admin - 08.01.2018 10:52 - Linux, Полезные программы

Потоковый редактор sed (stream editor) с момента разработки в 1974 году и по сей день является одним из самых популярных инструментом для обработки текста. Рассмотрим на примерах наиболее востребованные приемы его использования. Но, прежде всего, уточним следующее - все примеры, приведенные в данной статье, проверены на работоспособность. Для этого использовались утилита echo из пакета coreutils 8.28-1, GNU sed 4.4-1 и bash 4.4.012-2. Это самые актуальные версии на момент написания статьи. И все это под Arch Linux. Если у вас что-либо не работает так, как описано, то связано это с особенностями реализации какой либо из компонент используемой вами операционной системы. Тем не менее, мы будем использовать решения, работающие в большинстве случаев.

Итак, в общем случае синтаксис такой:

sed параметры [файл]

Если файл не указан, информация берется со стандартного ввода, что позволяет использовать sed в связке с другими утилитами при конвейерной обработке. Например:

echo 'Какой-нибудь текст' | sed 's/текст/другой текст/'

выведет

Какой-нибудь другой текст

В данном случае мы использовали команду 's' редактора sed для поиска и замены слова "текст" в строке "Какой-нибудь текст" на фразу "другой текст". Для совсем начинающих пользователей Linux поясним, что echo используется для вывода текста на стандартный вывод (попросту говоря, на экран), а '|' - это реализация конвейерной обработки, когда результат выполнения выводится не на экран, а используется как исходная информация для следующей команды. Конвейер может быть как угодно длинным и многоступенчатым, что позволяет реализовать даже очень сложную обработку, с использованием различных утилит, одной строкой.

Усложним пример:

echo 'Какой-нибудь текст и еще текст' | sed 's/текст/другой текст/'

Получим:

Какой-нибудь другой текст и еще текст

Как видим, замена произошла однократно, при первом срабатывании условия. Чтобы использовать замену всех вхождений, добавим модификатор 'g' в наш пример:

echo 'Какой-нибудь текст и еще текст' | sed 's/текст/другой текст/g'

Результат:

Какой-нибудь другой текст и еще другой текст

Проверим поведение редактора при использовании многострочного текста. Для этого используем команду echo с параметром -e (разрешить интерпретацию так называемых backslash escapes - специальных последовательностей символов, начинающихся с обратного слэша '\') и включим в текст перевод строки \n:

echo -e 'Какой-нибудь текст и еще текст\nа также текст в другой строке после которого тоже текст' | sed 's/текст/другой текст/'

Мы должны увидеть следующее:

Какой-нибудь другой текст и еще текст
а также другой текст в другой строке после которого тоже текст


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

echo -e "Какой-нибудь текст и еще текст\nа также текст в другой строке после которого тоже текст" | sed 's/текст/другой текст/'

или

echo $'Какой-нибудь текст и еще текст\nа также текст в другой строке после которого тоже текст' | sed 's/текст/другой текст/'

или даже так:

printf "Какой-нибудь текст и еще текст\nа также текст в другой строке после которого тоже текст\n" | sed 's/текст/другой текст/'

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

Посмотрим на результат при использовании многострочного текста. Заметили интересную особенность? А именно - замена произошла для каждого первого вхождения в каждой строке. Чтобы заменить все глобально, используем уже знакомый нам модификатор 'g':

echo -e 'Какой-нибудь текст и еще текст\nа также текст в другой строке после которого тоже текст' | sed 's/текст/другой текст/g'

Результат именно такой, как ожидалось:

Какой-нибудь другой текст и еще другой текст
а также другой текст в другой строке после которого тоже другой текст


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

Это важно запомнить для понимания поведения sed, особенно при использовании для более сложных вариантов обработки. Однако, как мы видели на примере использования модификатора 'g', поведение редактора можно изменить. Позже мы рассмотрим другие приемы, позволяющие это сделать.

Теперь, как и обещали, рассмотрим использование sed для обработки файлов. Создадим текстовый файл test.txt следующего содержания:

Какой-нибудь текст и еще текст
а также текст в другой строке после которого тоже текст


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

sed 's/текст/другой текст/' ./test.txt

Результат:

Какой-нибудь другой текст и еще текст
а также другой текст в другой строке после которого тоже текст


Измененный текст выводится в данном случае на экран, но файл остается неизменным. Чтобы изменения произошли непосредственно в файле, добавим параметр '-i':

sed -i 's/текст/другой текст/' ./test.txt

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

Какой-нибудь другой текст и еще текст
а также другой текст в другой строке после которого тоже текст


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

printf "Какой-нибудь текст и еще текст\nа также текст в другой строке после которого тоже текст\n" > ./test.txt

Затем выполним:

sed -e 's/текст/другой текст/;s/также/кроме того/' ./test.txt

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

Какой-нибудь другой текст и еще текст
а кроме того другой текст в другой строке после которого тоже текст


В качестве разделителя для команд используется символ ';'. Тот же результат можно получить следующим образом:

sed -e '
> s/текст/другой текст/
> s/также/кроме того/' ./test.txt

А еще список выполняемых команд можно поместить в файл. Создадим файл sedcommands следующего содержания:

s/текст/другой текст/
s/также/кроме того/


и выполним:

sed -f sedcommands ./test.txt

Имеем тот же результат, что и в предыдущем случае.

Модификаторы

Модификаторы (или substitution flags), служат для изменения поведения команды при выполнении. Общий синтаксис таков:

команда/шаблон/заместитель/модификаторы.

Мы уже использовали подобную конструкцию с модификатором:

sed 's/текст/другой текст/g'

В данном примере:

команда - 's' (поиск и замена)
шаблон - 'текст' (то, что ищется в тексте)
заместитель - 'другой текст' (то, на что замещается найденное)
модификатор - 'g' (замена для всех найденных совпадений)

Есть и другие модификаторы. Например, номер совпадения для обработки. Для иллюстрации вернемся к ранее использованному примеру:

sed 's/текст/другой текст/' ./test.txt

Какой-нибудь другой текст и еще текст
а также другой текст в другой строке после которого тоже текст


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

sed 's/текст/другой текст/1' ./test.txt

'1' задает поведение, при котором обработка производится для каждого первого совпадения в каждой строке.

Если использовать такой вариант:

sed 's/текст/другой текст/2' ./test.txt

то результат будет таким:

Какой-нибудь текст и еще другой текст
а также текст в другой строке после которого тоже другой текст


Заметили разницу? Замены произведены для каждого второго совпадения в каждой строке. Номер совпадения можно указать любой. Если совпадения с таким номером не будет найдено, то и замены не произойдет.

Также существует модификатор 'w' для записи результата выполнения в файл. Например:

sed 's/текст/другой текст/w result.txt' ./test.txt

В результате на экране мы получим уже знакомый нам модифицированный текст:

Какой-нибудь другой текст и еще текст
а также другой текст в другой строке после которого тоже текст


но кроме этого будет создан в текущем каталоге файл result.txt, содержащий этот же текст. Проверяем:

cat result.txt

видим:

Какой-нибудь другой текст и еще текст
а также другой текст в другой строке после которого тоже текст

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

sed 's/текст/другой текст/' ./test.txt > result.txt

Использование нестандартного разграничителя


Во всех предыдущих примерах мы использовали разграничитель '/', принятый по умолчанию. Но это не всегда удобно. Например, нужно заменить /bin/sed на /usr/bin/sed. Используемые в тексте символы '/' конфликтуют с символом разграничителя. Можно конфликтующие символы экранировать при помощи специального символа '\'. При этом команда будет выглядеть так:

sed 's/\/bin\/sed/\/usr\/bin\/sed/' ./test.txt

Это будет работать, но смотрится ужасно. Однако, если записать команду так:

sed 's!/bin/sed!/usr/bin/sed!' ./test.txt

или так

sed 's@/bin/sed@/usr/bin/sed@' ./test.txt

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

Ограничение диапазона обрабатываемых строк


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

  • указанием конкретного номера или номеров строк
  • заданием шаблона, который соответствует определенной строке

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

sed '2s/текст/другой текст/' ./test.txt

получаем:

Какой-нибудь текст и еще текст
а также другой текст в другой строке после которого тоже текст


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

sed '2s/текст/другой текст/2' ./test.txt

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

Какой-нибудь текст и еще текст
а также текст в другой строке после которого тоже другой текст


Для дальнейших экспериментов со строками нужно дополнить наш тестовый файл. Приведем его содержимое к следующему виду:

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


Продолжим эксперименты:

sed '2,4s/текст/другой текст/' ./test.txt

изменения произошли в диапазоне строк со второй по четвертую включительно.

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


Укажем диапазон начиная с четвертой строки и до конца файла:

sed '4,$s/текст/другой текст/' ./test.txt

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


С использованием шаблона:

sed '/номер 4/s/текст/другой текст/' ./test.txt

затронута только строка, содержащая шаблон 'номер 4':

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


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

Удаление строк


Кроме поиска и замены sed умеет производить и другие операции, в том числе удаление строк. Для этого существует команда 'd' (delete). Например, удалим третью строку:

sed '3d' ./test.txt

на экране видим, что третьей строки нет:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст
строка номер 4 в которой есть текст содержащий текст
пятая строка - текст, текст и еще раз текст


А что в файле?

cat ./test.txt

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


В файле строка осталась на месте. Такое поведение полезно, если мы используем файл как источник исходной информации для дальнейшей обработки и, возможно, сохранения результата в другом месте. Для изменения исходного файла используем знакомый нам параметр '-i':

sed -i '3d' ./test.txt

проверяем:

cat ./test.txt

и видим, что строка из файла удалена:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст
строка номер 4 в которой есть текст содержащий текст
пятая строка - текст, текст и еще раз текст


Восстановим тестовый файл и продолжим:

sed '2,4d' ./test.txt

удалены строки со второй по четвертую:

Какой-нибудь текст и еще текст
пятая строка - текст, текст и еще раз текст


Удалим строки с третьей и до конца файла:

sed '3,$d' ./test.txt

выполнено:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст


Удаление строки, соответствующей шаблону:

sed '/вторая строка/d' ./test.txt

результат, как и заказывали (нет второй строки. А вернее строки, содержащей текст 'вторая строка'):

Какой-нибудь текст и еще текст
это третья строка, содержащая текст и текст
строка номер 4 в которой есть текст содержащий текст
пятая строка - текст, текст и еще раз текст


Для удаления диапазона строк по шаблону:

sed '/вторая строка/,/содержащий текст/d' ./test.txt

Удалены строки со второй по четвертую:

Какой-нибудь текст и еще текст
пятая строка - текст, текст и еще раз текст


Вставка строк


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

  • i (insert)
  • a (append)

В переводе на русский insert - это вставка, append - добавление. Разница не слишком очевидна. Нужно просто запомнить, что 'i' - это вставка до указанной строки или текста, в то время как 'a' - это добавление строки после.

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

sed '2i\Новая строка' ./test.txt

результат соответствует ожиданиям:

Какой-нибудь текст и еще текст
Новая строка
вторая строка: текст после которого тоже текст
это третья строка, содержащая текст и текст
строка номер 4 в которой есть текст содержащий текст
пятая строка - текст, текст и еще раз текст


Вставка после второй строки:

sed '2a\Новая строка' ./test.txt

тоже без сюрпризов:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст
Новая строка
это третья строка, содержащая текст и текст
строка номер 4 в которой есть текст содержащий текст
пятая строка - текст, текст и еще раз текст


Изменение строк


Аналогично добавлению, но используется команда 'c'. Например:

sed '3c\Это измененная третья строка' ./test.txt

видим:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст
Это измененная третья строка
строка номер 4 в которой есть текст содержащий текст
пятая строка - текст, текст и еще раз текст


Замена строк, соответствующих шаблону:

sed '/еще/c Это измененная строка' ./test.txt

Заменены все строки, где найден шаблон 'еще' (первая и пятая):

Это измененная строка
вторая строка: текст после которого тоже текст
это третья строка, содержащая текст и текст
строка номер 4 в которой есть текст содержащий текст
Это измененная строка


Замена символов


Формат команды:
sed 'y/символы шаблона/символы замены/'. Например:

echo "1900 1901 1902 1934" | sed 'y/12345/67890/'

даст результат:

6900 6906 6907 6989

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

Практическая польза такой операции не совсем очевидна. Ну, к примеру, можно добавить немножко "немецкого акцента" к фразе:

echo "сколько времени?" | sed 'y/св/шф/'

имеем:

школько фремени?

Отображение номеров строк


Команда:

sed '=' ./test.txt

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

1
Какой-нибудь текст и еще текст
2
вторая строка: текст после которого тоже текст
3
это третья строка, содержащая текст и текст
4
строка номер 4 в которой есть текст содержащий текст
5
пятая строка - текст, текст и еще раз текст


т.е. перед каждой строкой отображается ее номер. Полезно для отладки.

В комбинации с параметром -n выводит только номера строк, соответствующих шаблону:

sed -n '/еще/=' ./test.txt

результат:

1
5


Получение данных из файла


Создадим файл с названием readfile.txt, содержащий 2 строки:

printf "Первая строка из файла\nВторая строка из файла\n" > readfile.txt

И выполним команду:

sed '4r readfile.txt' ./test.txt

в результате содержимое файла readfile.txt будет добавлено после 4-й строки исходного файла:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст
это третья строка, содержащая текст и текст
строка номер 4 в которой есть текст содержащий текст
Первая строка из файла
Вторая строка из файла
пятая строка - текст, текст и еще раз текст


То же самое, но с использованием шаблона:

sed '/номер 4/r readfile.txt' ./test.txt

результат:

Какой-нибудь текст и еще текст
вторая строка: текст после которого тоже текст
это третья строка, содержащая текст и текст
строка номер 4 в которой есть текст содержащий текст
Первая строка из файла
Вторая строка из файла
пятая строка - текст, текст и еще раз текст


Итог


Мы рассмотрели только некоторые основные приемы использования редактора sed. На деле же его возможности, особенно в сочетании с другими утилитами, наподобие awk, с использованием регулярных выражений и прочего доступного инструментария ограничены в обработке текста, пожалуй, только вашим воображением. Обычное применение - автоматическая обработка конфигурационных и лог-файлов. Незаменим для обработки очень больших файлов, которые обычный интерактивный редактор попросту не в состоянии открыть из-за нехватки оперативной памяти. Хорош для автоматической обработки информации о текущем состоянии системы, и т.п. Уверен, зная как это работает, вы точно найдете ему применение. Удачи.

Пожалуйста, войдите в систему для комментирования.

3 комментариев

pavel

11.01.2018 12:22

блин, как все просто оказалось ... Спасибо!


admin

10.01.2018 12:18

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

 sed "/третья строка/{n;s/.*/Это новая строка/}" ./test.txt

результат:

 Какой-нибудь текст и еще текст

вторая строка: текст после которого тоже текст

это третья строка, содержащая текст и текст

Это новая строка

пятая строка - текст, текст и еще раз текст

т.е. мы ищем строку по шаблону 'третья строка' и в строке, следующей после нее (команда 'n') делаем замену всего ее содержимого ('.*' соответствует всем символам в строке) на 'Это новая строка'


pavel

10.01.2018 11:58

А можно ли заменить строку, следующую за той, которая соответствует шаблону? Недавно убил почти полдня на поиск решения, но безрезультатно.