Основы sed


Без редакторов sed и awk работать с bash сложно.
Поэтому рано или поздно возникает необходимость "знать и уметь".

sed - потоковый редактор, который работает со строками. Параметры его потоков это:

* строка на входе (Input Stream)
* строка в оперативной ячейке (Pattern Space)
* буфер, в который можно поместить или добавить строку (Hold Buffer)
* строка на выходе (Output Stream)

Все команды sed - это буквы:

a b c d D g G h H i n N p P q Q r s t w x y =

Каждая - определяет некоторую операцию. Как правило, большинство команд sed применяются
к оперативной ячейке и выходному потоку. Т.е. если мы напишем что-то типа

sed -r 's/file/replace/' file.txt,

то подразумевается, что замена происходит для строки, находящейся в оперативной ячейке.

Для входного потока действуют только две команды:

n - взять строку и поместить ее в оперативную ячейку
N - взять строку и добавить ее к оперативной ячейке

На буфер влияют еще три команды

h - поместить оперативную ячейку в буфер
H - добавить оперативную ячейку к буферу
x - обменять местами буфер и оперативную ячейку

На выходной поток влияют команды:

= - напечатать номар строки
a - добавить текст после строки вывода
i - добавить текст до строки вывода
c - заменить текст вывода на указанный
p - вывести текущую оперативную ячейку
P - вывести первую строку из оперативной ячейки
r - добавить текст из файла
R - добавить строку из файла (GNU)
w - записать оперативную ячейку в файл
W - записать первую строку из оперативной ячейки в файл

К оперативной ячейке можно применить команды:

d - удалить содержимое оперативной ячейки. начать новый цикл
D - удалить первую строку из оперативной ячейки.
    начать новый цикл, но вход не брать, пока ячейка не пуста
g - скопировать буфер в оперативную ячейку
G - добавить буфер к оперативной ячейке
s - произвести замену
y - произвести транслитерацию

И есть еще условные и управляющие команды, позволяющие организовывать логику:

{} - определить блок команд
b  - перейти на метку
t  - перейти на метку или в конец скрипта, если предыдущий оператор s успешен
T  - перейти на метку или в конец скрипта, если предыдущий оператор s не успешен
q  - завершить скрипт, но перед этим допечатать то, что осталось на входе (GNU)
Q  - завершить скрипт немедленно

Есть также как минимум два важных ключа, влияющих на работу и удобство использования sed:

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

Чтобы понять логику работы, рассмотрим такой простой скрипт <tt>test.sed</tt>:

#!/bin/sed -rnf

1 {
  h
  b eof
}

$ {
  =
  x
  p
  Q
}

H

:eof

Его работа заключается в том, что после вызова ./test.sed somefile
он выводит на экран число строк в файле somefile, а затем все строки
файла, кроме последней. Алгоритм простой:

* Если встречаем первую строку - помещаем ее в буфер (h), выходим через метку eof (b)
* Если встречаем последную строку - выводим ее номер (=), достаем всё, что есть в буфере (x),
  выводим (p), завершаем (Q)
* По умолчанию - добавляем очередную строку к буферу (H)

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

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

# Плохо
s/очень сложное выражение/замена/

# Хорошо
s/менее сложное выражение/~/; s/~/замена/


2. Одинаковые символы, слова и строки можно искать одним простым выражением

# Найти два одинаковых символа в строке
/(.)1/

# Найти две одинаковых строки
/(.*)n1/

3. Анализ содержимого строки можно делать операцией <tt>s/...//;T</tt>

# если в строке есть 3 слова linux - то отметить такие строки !
sed -r 's/(linux).*1.*1//;T;s/.*/!/' file.txt

4. Поскольку часто приходится рабоать с таблицами, то доступ к колонке
   также можно получать простым выражением:

# Удалить первые три колонки
s/(S+s+){3}//

# Оставить четвертую и пятую
s/(S+s+){2}.*/1/

5. Очень многие разные задачи решаются одним и тем же приемом - G-s-h-p
   Вот, к примеру, скрипт, который из списка слов выдаёт только уникальные слова:

#!/bin/sed -rnf

G; s/(^[^n]+n)(.*)1/12/; h

$ {
  p
}

На этом всё!