включить редактирование

Редактор sed в Linux

Создан:

Рассмотрим сегодня популярный в Linux потоковый редактор Sed. Он широко применяется в скриптах для системного администрирования и очень будет Вам полезен.
Кратко из истории.
Sed очень стар. Он был написан Ли Мак-Мэхоном [Lee Е McMahon] в 1974 году и появился в седьмом издании «Руководства по программированию в Unix» в 1979 году. Sed представлял собой результат развития интерактивного редактора строк ed, и команды Sed, которые сегодня кажутся странными, были гораздо более комфортными для опытных пользователей ed (или даже для тех из Вас, кому знакомы основные команды vi). Даже версии Sed для GNU уже 15 лет — она появилась в 1998 году. Sed, в свою очередь, оказал влияние на другие языки обработки текста, в особенности на Perl.
Вы можете возразить, что набор команд Sed нельзя считать языком программирования. Но знаменитый скрипт Кристофа Блесса [Christophe Blaess] показывает, что Sed является «полным по Тьюрингу», то есть способен (теоретически) приблизительно смоделировать любой другой язык программирования общего назначения. Юлия Йомантайте [Julia Jomantaite] даже написала Тетрис на Sed.
Начнем с самого начала. Sed — потоковый редактор. Он ведет себя как классический фильтр: если передать ему файл, Sed берет входной поток с этого файла. Если файл не указан, Sed читает стандартный ввод, что позволяет разместить его на выходе канала для обработки вывода какой-либо другой команды.
Независимо от того, поступают ли данные из файла или из стандартного потока (stdin), Sed считывает одну строку входного потока, выполняет над ней заданный набор операций редактирования и записывает результирующую строку в стандартный вывод (stdout). Затем он считывает следующую строку, и все начинается по новой. В отличие от большинства интерактивных редакторов, считывающих весь файл в буфер, Sed обрабатывает его построчно, что позволяет эффективно работать с очень большими файлами.
Подстановка в Sed
Начнем с простого примера, в котором Sed выполняет подстановку — это, наверное, самое популярное его применение. Пусть мы переместили домашние каталоги пользователей из /home в /users, и приходится менять все имена домашних каталогов в /etc/passwd. То есть, строки вида

chris:x:501:501::/home/chris:/bin/bash нужно изменить на
chris:x:501:501::/users/chris:/bin/bash

Эту работу сделает команда
sed s/home/users/ /etc/passwd

Проясним, что здесь происходит. Sed строка за строкой считывает файл с паролями, выполняет замену в каждой строке и записывает результат в stdout. Он не меняет исходный файл. Если надо изменить исходный файл, соблазнительно попробовать такое:
sed s/home/users/ /etc/passwd > /etc/passwd

Но этот путь ведет к катастрофе. Увидев перенаправление вывода, оболочка обрежет выходной файл до нуля, прежде чем Sed его увидит. Прощай, файл с паролями! Это справедливо и в целом для фильтров — их вывод нельзя перенаправить обратно в исходный файл. Вместо этого можно сделать нечто вроде такого:
sed s/home/users/ /etc/passwd > /tmp/passwd
mv /tmp/passwd /etc/passwd

На самом деле, в GNU-версии Sed есть параметр -i (in place — «вернуть на место»), который позволяет обойтись без временного файла. Поэтому команда
sed -i s/home/users//etc/passwd

тоже сработает, хотя вообще-то я не советовал бы вам экспериментировать с файлом паролей, если вы не уверены в том, что команда Sed делает именно то, что вы думаете.
Наш следующий пример и того проще. Команда df генерирует удобную таблицу с информацией об использовании диска для каждой файловой системы но у этой таблицы есть строка заголовка, мешающая последующей обработке данных. Эту первую строку можно удалить так:
df | sed 1d

Здесь Sed считывает данные из стандартного ввода (перенаправленного вывода df). Команда d означает «удалить (delete] строку», а 1 — «сделать это только для строки 1». Поэтому первая строка будет отрезана, а все остальные останутся нетронутыми. Эта команда эквивалентна команде tail -n +2.
Вернемся к команде замены s (substitute). Предположим, что из /etc/passwd вам нужно получить только имена пользователей — то есть поле до первого двоеточия. Это легко, если знать, что компонент замены «старый шаблон» может быть регулярным выражением.
sed s/:.*// /etc/passwd

Наш пример немного обманчив. «Старый шаблон» — регулярное выражение :. , которое соответствует части строки от первого двоеточия и до конца строки. (Здесь мы рассчитываем на «жадность» regex a — оно начинает искать совпадения как можно раньше и ищет их до последней возможности). «Новый шаблон» пуст, поэтому все, что соответствует регулярному выражению, будет удалено. Волшебство!
Приведем еще один пример с заменой. Предположим, вы хотите изменить строки вида " £25" на " 25 GBP" , что сложнее, так как текст " GBP" должен появиться после числа. Текст, подобный оплата обычно составляет от £20 от £40 заменится на
оплата обычно составляет от 20 GBP до 40 GBP посредством команды
sed-r 's/£([0-9]*)/\1 GBP/g'  prices

Я нарисовал схему, чтобы пояснить, как это работает.
sed
Ну как, сложно? Пожалуй, да но многие команды Sed, которые вы встретите в скриптах, содержат регулярные выражения и позамысловатее. Вот пример из файла /etc/init/rc-sysinit.conf в Ubunlu:
sed -nre  's/^[^#][^:]*:([0-6sS]):initdefault:.*/DEFAULT_
RUNLEVEL=" \1" /p' /etc/inittab

Эта команда извлекает уровень выполнения по умолчанию из файла inittab.
Для разделения частей команды замены обычно применяется прямой слэш, но если старый или новый шаблоны сами содержат прямые слэши, команда может стать трудной для понимания. Предположим, мы хотим заменить /home/chris/bin: на /opt/bin . Придется экранировать все прямые слэши, и команда примет вид
 sed  's/\/home/\chris\/bin\/\/opt\/bin\/'  foo.txt

Если использовать другой разделитель (в данном случае : ), команда немного упростится:
sed 's:/home/chris/bin:/opt/bin:'  foo.txt

Выбор строк
В Sed можно выбрать отдельные строки или диапазоны строк, над которыми должна выполняться команда. Ранее мы пользовались командой 1d для выбора первой строки — 1. Также можно было выбрать диапазон: 1,10d для удаления первых 10 строк или 5,$d для удаления строк с 5-й по последнюю {$ — обозначение последней строки в файле). Также можно выбирать строки с помощью регулярного выражения: команда
sed  '/^#/d'  /etc/fstab

удалит строки с # в начале (обычно это комментарии). Это нечто вроде grep наоборот (выводятся строки, не отвечающие выражению). Чтобы получить обычную grep, нужно выполнить два изменения: задать параметр -n, отключающий автоматический вывод строк, и явно попросить Sed напечатать только желаемые строки:
sed -n '/^#/р' /etc/fstab

Обратите внимание, что я заключил команду в одиночные кавычки, чтобы не было войн между метасимволами, так легко возникающих в командной строке Linux.
Вот пример поинтереснее. Пусть у нас есть скрипт с несколькими определениями функций, и нужно извлечь функции в отдельный файл. Для иллюстрации представим себе такой игрушечный скрипт:
#!/bin/bash
echo hello
function foo() {
  echo this is too
}
#call the first function
foo
function bar() (
  echo this is bar
}
#call the second function
bar

Сначала создадим скрипт с вырезанными определениями функций:
sed  '/^function/,/^}/d' demo.sh >  demo2.sh

Здесь мы указываем диапазон номеров строк на основе регулярного выражения. Текст между строкой, начинающейся с функции, и строкой, начинающейся с символа " }" , удаляется. Если в файле несколько таких блоков, все они будут удалены. Развернув логику наоборот, можно удалить только определения фукций:
sed -n '/^function/,/^}/p' demo.sh >  funcs.sh 

С grep такое сделать нельзя!
Буфер шаблона и буфер захвата
Даже немногими командами, с которыми мы познакомились, в сочетании с осторожным использованием регулярных выражений в Sed можно сделать очень многое. Но во всех наших примерах выходные строки будут появляться в том же порядке, что и входные. Порядок строк в файле остается неизменным. Чтобы его изменить, нужно познакомиться с «буфером шаблона» и «буфером захвата». Буфер шаблона — обычный текстовый буфер, который используется для обычного построчного редактирования. Например, команда замены работает с буфером шаблона, а команда р выводит содержимое буфера шаблона.
sed
Буфер захвата по сути представляет собой буфер, где мы можем размещать текст сами, что позволяет изменить порядок содержимого входного потока.
Для переноса текста в буфер захвата и извлечения текста из него используются три основных команды — h, Н и х.
Для использования буфера захвата обычно приходится запускать две или несколько команд Sed в одном вызове прежде чем двигаться дальше, посмотрим, как это делается. Первый способ — указать параметр -е в командной строке. Например, команда
sed -е  's/linux/windows/'-е 's/good/bad/'  somefile.txt
выполнит обе замены в каждой строке. Другой способ — разделить команды точкой с запятой таким образом:
sed  's/linux/windows/;s/good/bad/'  somefile.txt

Такие подходы прекрасно работают, но если команд больше, чем две или три, оно становится утомительно. Лучший вариант — поместить команды в файл и указать его в командной строке. Перепишем наш пример, используя этот подход. Для этого создадим файл с именем (например) script.sed, содержащий
s/linux/windows/ 
s/good/bad/

Затем велим Sed брать команды из этого файла:
sed -f script.sed somefile.txl

Помещение команд Sed в отдельный скрипт обладает рядом преимуществ. Во-первых, команды больше не нужно заключать в кавычки, так как они уже не интерпретируются оболочкой. Еще одно преимущество в том, что скрипт — это компонент, допускающий многократное употребление.
Помня все это, вернемся к нашему примеру и поставим себе немного другую задачу. Предположим, что нужно просто переместить все определения функций в начало файла, а за ними должно идти все остальное. Вот скрипт — в нем всего три строки:
# Скрипт Sed для помещения наших функций в скрипт оболочки
/^function/,/^}/!H
/^function/,/^}/p
$(х;р;}

Пример функции сдвига
Наверное, необходимо некоторое пояснение.
В первой строке используется та же пара регулярных выражений для определения тела функции, которой мы пользовались раньше мы только добавили ! для изменения значения условия на противоположное. Команда Н добавляет содержимое буфера шаблона в буфер захвата. Таким образом, она помещает в буфер захвата все строки, которые находятся за пределами определения функции.
Вторая строка выводит строки, которые находятся внутри определения функции (чтобы они шли первыми, как и требуется).
Наконец, в последней строке используется обозначение $, символизирующее последнюю строку ввода. Команда помещает содержимое буфера захвата в буфер шаблона и выводит его.
Запустим его и посмотрим, что произойдет:
$ sed -n -f splitout.sed demoscript.sh 

function foo() ( 
  echo this is foo
}
function bar() {
  echo this is bar
}
#!/bin/bash
echo hello
#   Вызов первой функции
foo
#   Вызов второй функции
bar

Все почти правильно — проблема только в том, что строка #! все-таки должна быть вверху. Это не слишком сложно исправить — оставляю это вам!

Sed в реальном мире
Если вы думаете, что Sed слишком непонятен, чтобы с ним связываться, вот немного статистики. Я подсчитал, сколько раз Sed используется в скриптах системного администрирования в Ubuntu. Правду-то сказать, это сделала команда
$ find /etc -type f -exec grep -w sed {}\;2>  /dev/null | wc -L 
Примеров оказалось 259.
В большинстве этих примеров Sed используется в команде замены, чтобы задать значение переменной из содержимого конфигурационного файла, что-то вроде
pid=$(sed 's/ //g'  /var/spool/postfix/pid/master.pid)

Этот пример всего-навсего удаляет пробелы из входных данных. Ключ g в конце говорит о том, что изменения должны быть глобальными, т. е. должны быть выполнены везде в пределах данной строки.
Другой распространенный вариант использования Sed — взять значение какой-нибудь существующей переменной и как-нибудь изменить его. Этот пример взят из /etc/network/if-pre-up.d/ vlan в Ubuntu:
VLANID=`echo $IFACE|sed "s/vlan()*//"`

Обратите внимание на альтернативный способ выделения замены кавычками.
А вот еще один пример, совместно использующий Awk и Sed:
arch=`echo " $line" |awk '{print $4}' | sed  's/:$//'` 
Здесь Awk выбирает четвертое поле строки $line, a Sed удаляет замыкающее двоеточие.
И, наконец, следующий шедевр я позаимствовал из /etc/ bash_completton.d/sysv-rc; 
valid_options=( $(\ 
tr"""\n"<<<"${COMP_WORDS[@]} ${options[@]}" \ 
| sed -ne "/$( sed "s/ /\\|/g" <<<"$(options[@]}")/p" \ 
| sort | uniq -u\ 
))

Этот впечатляющий фрагмент использует Sed в команде замены, чтобы сгенерировать команду для внешней команды Sed. У меня голова идет кругом при одной мысли об этом.
Я был не вполне честен, приводя этот пример вне контекста. Мы не знаем, на что похожа структура входных данных, поэтому трудно понять, что происходит. По моему опыту, ключ к пониманию все этих причудливых вывертов обработки текста — очень четкое представление о структуре данных, которые Вы обрабатываете на каждом этапе.
На этом коротко о sed все.

Автор: DJek Просмотров: 9344


Рейтинг статьи: 0

Общий рейтинг по отношению ко всем статьям автора :
{0 [0]}[max] [ - - - - - - - - - - ]

Общий рейтинг из всех статей на сайте :
{0 [888]} [ - - - - - - - - - - ]

[?]
комментариев к данной статье нет

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


Ctrl+Enter

Для активации кнопки, введите символы, которые Вы видите на картинки.

новая

тема

Заметки на тему IT

Монитор поиска
[x]
Новое сообщение

Сообщения в чате

Вы спрашиваете у гостей/у зарегистрированных/ У Вас спрашивают
всем Ctrl+Enter
зарегистрированным Ctrl+Enter
Ctrl+Enter

Краткая инструкция по работе с чатом

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