Всем привет.
Говорят что bash без применения утилит обработки текста sed и awk такой же скучный как осенний дождливый день. Однако чтобы разогнать тучи надо совсем немного - научиться понимать простейшие конструкции sed и awk и нескольких базовых команд.
Мы напомним что sed это неинтерактивный редактор текстовых файлов, а аwk - это язык обработки шаблонов с C-подобным синтаксисом.
При всех своих различиях, эти две утилиты обладают похожим синтаксисом, они обе умеют работать с регулярными выражениями, обе, по-умолчанию, читают данные с устройства stdin и обе выводят результат обработки на устройство stdout. Обе являются утилитами UNIX-систем, и прекрасно могут взаимодействовать между собой. Вывод от одной может быть перенаправлен, по конвейеру, на вход другой. Их комбинирование придает сценариям, на языке командной оболочки, мощь и гибкость языка Perl.
Сегодня мы рассмотрим sed, а аwk будет позже.
Базис.
Итак 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
}
Эмуляция известных команд в sed.
Эмуляция grep pattern
sed -n '/pattern/p'
sed '/regexp/!d'
Эмуляция grep -v pattern
sed -n '/pattern/!p'
Эмуляция egrep
sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
Эмуляция head
sed 10q
Эмуляция head -n 1
sed q
Эмуляция tail
sed -e :a -e '$q;N;11,$D;ba'
Эмуляция tail -n 1
sed '$!d'
sed -n '$p'
Эмуляция tail -n 2
sed '$!N;$!D'
Эмуляция cat -s
sed '/./,/^$/!d'
Эмуляция wc -l (Счетчик строк)
sed -n '$='
Эмуляция tac (Реверсивный cat)
sed '1!G;h;$!d'
sed -n '1!G;h;$p'
Эмуляция nl (нумерация)
sed = filename | sed 'N;s/n/t/'
Эмуляция rev (переворот строки)
sed '/n/!G;s/(.)(.*n)/&21/;//D;s/.//'
Эмуляция uniq (уникальные строки)
sed '$!N;/^(.*)n1$/!P; D'
Эмуляция uniq -d (дубли строк)
sed '$!N;s/^(.*)n1$/1/;t;D'
Рецепты sed.
Как и в случае с другими командами - главное для sed - это опыт, который можно получить только практикой. А практика начинается с обычного текстового файла, который можно создать в обычном консольном редакторе типа nano и затем использовать как материал для sed. У меня это выглядело так:
# исходный файл
cat file.txt | nl
1 москва
2 питер
3 санкт петербург
4 киев
5 минск
6 новгород
7 уфа
8 казань
9 иркутск
10 сергиев посад
11 павловский посад
12 калязин
13 углич
14 самара
15 владимир
16 суздаль
17 кострома
Дальше я начал свои эксперименты:
# вывести строку N3
sed -n 3p file.txt
# удалить строку N3 (cанкт петербург)
sed 3d file.txt
# вывести "посады"
sed -n '/посад/p' file.txt
# вывести города на "са"
sed -n '/^са/p' file.txt
# удалить пустые строки
sed '/^$/d' file.txt
# удалить все строки "посад"
sed '/посад/d' file.txt
# удалить все слова "посад" в строках
sed 's/посад//g' file.txt
# удалить все строки до первого "посад"
sed 1,'/посад/d' file.txt
# удалить все строки до первой пустой
sed 1,'/^$/d' file.txt
# заменить посад на ПОСАД
sed 's/посад/ПОСАД/' file.txt
# заменить только павлов посад на ПОСАД
sed '/павл/s/посад/ПОСАД/' file.txt
# в каждой строке заменить все "о" на "О"
sed 's/о/0/g' file.txt
# удалить все пробелы в конце строки
sed 's/ *$//' file.txt
Я не стал накручивать большие регулярные выражения - но это возможно и легко, поскольку параметры основных команд sed (p,d,s,y) - могут являться регулярными выражениями. Продолжаем изыскания:
# добавляем пустые строки
sed G file.txt
# удалить каждую третью строку
sed 'n;n;d' file.txt
# количество строк в файле
sed -n '$=' file.txt
# добавляем строки с номерами
sed = file.txt
# удаляем пробелы и табуляции с конца и с начала
sed 's/^[ t]*//;s/[ t]*$//' file.txt
Фуух, вот так. Успехов.
No comments:
Post a Comment
А что вы думаете по этому поводу?