LINUX.ORG.RU

bash: вопрос по использованию sed


0

0

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

filename=test.txt
res=`sed -e '/^$/d' $filename`
echo "$res" > $filename

Теперь нужно удалить не все пустые строки,
а только соседние задвоенные-затроенные и т.д.,
т.е. там, где есть несколько пустых строк подряд,
должна остаться только одна.

Sed якобы умеет получать несколько групп команд редактирования,
с использованием фигурных скобок.
Как этим пользоваться пока не понял и прошу помощи.
Решение нужно именно с помощью sed,
т.к. есть самоцель немного разобраться, как им управлять.

Я родил вот такое решение:
sed 'N;s/\(.*\)\(\n\1\)\+/\1/'
Проблема здесь в команде N - она читает следующую строку в текущую. Таким образом, в данном случае скрипт будет избавляться только от задвоенных строк. Для избавления от затроенных нужно добавить ещё одну N. Для зачетверённых - ещё одну, итд.
Подозреваю, что есть более красивые решения, просто первое, что пришло в голову. На awk, кстати, было бы проще.

Laz ★★★★★
()
Ответ на: комментарий от Laz

>> Я родил вот такое решение...
Ага, понятно. Но нужно всё-таки универсально,
чтобы для задесятерённых тоже работало.

http://gazette.linux.ru.net/rus/articles/abs-guide/a15696.html
Цитата:
Указание диапазона строк, предшествующее одной, или более,
инструкции может потребовать заключения инструкций в фигурные скобки,
с соответствующими символами перевода строки.
/[0-9A-Za-z]/,/^$/{
/^$/d
}
В этом случае будут удалены только первые из нескольких,
идущих подряд, пустых строк.
Это может использоваться для установки однострочных интервалов
в файле, оставляя, при этом, пустые строки между параграфами.

Однако. Во-первых, я вообще не понял, как же работают фигурные скобки.
Во-вторых, у меня это не сработало, как заявлено.
(Хотя заявлено очень мутно, видимо, я просто не понял.)
У меня, наоборот, удалились только одиночные,
а все задвоенные-затроенные остались.
(Может, так и было задумано автором.)

Вобщем, хотелось бы понять, как работают фигурные скобки.
И как быть мне.

The_gray_Cardinal
() автор топика
Ответ на: комментарий от Laz

> Подозреваю, что есть более красивые решения

напр. избавляться от задвоенных строк в цикле до тех пор, пока их не останется вовсе.

Komintern ★★★★★
()
Ответ на: комментарий от Komintern

>> напр. избавляться от задвоенных строк в цикле до тех пор,
>> пока их не останется вовсе.

По-моему, это наоборот, наименее красивое решение.

The_gray_Cardinal
() автор топика
Ответ на: комментарий от The_gray_Cardinal

Фигурные скобки, грубо говоря, группируют несколько операторов в один, т.е. проделывают несколько операторов над заданным диапазоном. В приведённом тобой примере ситуация несколько другая - пустые строки легче отличить от непустых.
Всё же проще воспользоваться awk:
awk 'BEGIN {PREV=""}; {if ($0 != PREV) {print $0; PREV=$0}}'

Laz ★★★★★
()
Ответ на: комментарий от Jini

>> /^$/{:a N; /[^\n]/{s,^\n\+,\n,; b}; b a}
Спасибо! Кажется, это то, что нужно.

Теперь осталась самая малость ;)
Понять, как это работает.
Русские мануалы по sed местами загадочны и сумбурны, например:
http://azarkevich.blogspot.com/2008/03/sed.html
Английские - ещё более загадочны, т.к.
английский я знаю точно хуже, чем русский.

Насколько я понимаю, sed работает построчно, т.е.
берёт строку, применяет к ней скрипт, затем следующую строку и т.д.

Первая часть /^$/ вроде понятна - взять строку, если она пустая.
Далее в фигурных скобках - всё, что надо сделать со взятой строкой, так?

Дальше одни вопросы.
Прошу помучиться с моими вопросами, не торопясь.
Буду спрашивать понемногу.

Первая команда:
:a N;
Это метка?
Что означает "пустое" N?
Эта команда вроде означает поместить что-то в "буфер редактирования"?
Что именно помещается в буфер здесь?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от Jini

$ echo 'abc
> abc' | sed '/^$/{:a N; /[^\n]/{s,^\n\+,\n,; b}; b a}'
abc
abc

Laz ★★★★★
()

Гм... Прошу прощения, я с самого начала неправильно прочитал условие. Мне привиделось, что нужно удалить любые повторяющиеся строки, а не только пустые.

Laz ★★★★★
()
Ответ на: комментарий от The_gray_Cardinal

>Что означает "пустое" N?
>Эта команда вроде означает поместить что-то в "буфер редактирования"?

>Что именно помещается в буфер здесь?


Из мана - append the next line of input into the pattern space, т.е. поместить следующую строку в пространство шаблона (если дословно)

Laz ★★★★★
()
Ответ на: комментарий от Laz

>> Мне привиделось, что нужно удалить любые повторяющиеся строки,
>> а не только пустые.

Вот и хорошо :)
У меня есть повод самостоятельно переписать скрипт :)

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

The_gray_Cardinal
() автор топика
Ответ на: комментарий от The_gray_Cardinal

> Первая часть /^$/ вроде понятна - взять строку, если она пустая. Далее в фигурных скобках - всё, что надо сделать со взятой строкой, так?

Так.

:a --- установить метку по имени 'a' для последующего перехода.

N --- взять следующую строку и добавить её в конец имеющейся. При этом между имеющейся строкой и добавляемой вставляется символ \n --- символ перевода строки.

/[^\n]/ --- если в получившейся строке есть хотя бы один символ не перевода строки, то выполнить следующие команды:

s,^\n\+,\n, --- удалить символы перевода строки в начале того, что у нас хранится в буфере.

b --- перейти в конец скрипта.

Наконец, b a --- перейти к метке a.

Операторы :a и b a создают цикл, который выполняется до тех пор, пока в добавляемой строке не обнаружится хотя бы один символ, отличный от перевода строки. Тогда сработает /[^\n]/ и выполнится оператор b, выводящий sed из цикла. Оператор s при этом удалит скопившиеся за время выполнения цикла символы \n в буфере, оставив лишь один.

Кстати, уже отписавшись я заметил, что такой скрипт не удалит строки в конце файла. Домашнее задание: исправить это поведение :)

Jini ★★
()
Ответ на: комментарий от Laz

>> Из мана - append the next line of input into the pattern space...

"следующая строка" - это в нашем случае
очередная найденная пустая строка,
по шаблону в самом начале скрипта?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от The_gray_Cardinal

>Но очень прошу ответить на мои вопросы и разобрать по косточкам то, что уже оказалось написано.

Хех, я сам ни разу не мастер по седу, я сейчас ещё не до конца осознал, что же написано в этом скрипте :) Однако капча houorra приободряет.

Laz ★★★★★
()
Ответ на: комментарий от The_gray_Cardinal

"Следующая строка" --- это строка, следующая в файле за последней прочитанной. Независимо от того, пустая она нет.

Jini ★★
()
Ответ на: комментарий от alexsaa

>> /^$/N;/^\n$/D
Спасибо, это тоже работает, и выглядит проще.
Есть вопрос, чтобы прояснить обработку последних пустых строк в файле.

Символ перевода строки с точки зрения sed
(и с точки зрения других программ, работающих "построчно")
является ли _составной_частью_ этой строки?
Если да, то где он находится с точки зрения регулярных выражений?

Например, ни одно из следующих выражений вроде не удаляет никакие пустые строки:
/^\n$/D
/\n^$/D
/^$\n/D
Почему?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от Jini

>> s,^\n\+,\n,
>> --- удалить символы перевода строки в начале того,
>> что у нас хранится в буфере.

Может, не удалить, а заменить такие множественные символы на один?
Кстати, здесь можно заменить запятые на слеши? (Это работает, вроде.)
Вообще, запятые равносильны слешам?

>> /[^\n]/
>> если в получившейся строке есть хотя бы один символ
>> не перевода строки...

А почему вместо [^\n] не работает просто точка?
Ведь вроде тоже самое?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от Jini

>> /[^\n]/
>> если в получившейся строке есть хотя бы один символ
>> не перевода строки...

Следует ли из этого, что содержимое буфера рассматривается sed'ом
всегда как одна строка, невзирая на переводы строк в тексте?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от Jini

>> Кстати, уже отписавшись я заметил, что такой скрипт не удалит строки в конце файла...

Наоборот, удаляет все подчистую. А должен был бы одну оставить.
GNU sed версия 4.1.5.
Кстати, непонятно, почему он так не делает.

The_gray_Cardinal
() автор топика
Ответ на: комментарий от fMad

>так и убить можно :) пусть бы сам дошёл

наблюдая за этой оргией с участием sed в главной роли, я бы так не сказал :)

dikiy ★★☆☆☆
()
Ответ на: комментарий от fMad

>> так и убить можно :)
>> пусть бы сам дошёл

Господа, для тех, кто не читал первый пост, не поленюсь повторить ;)
Решение нужно именно с помощью sed,
т.к. есть самоцель немного разобраться, как им управлять.

Мои вопросы чуть выше всё ещё в силе.

The_gray_Cardinal
() автор топика
Ответ на: комментарий от The_gray_Cardinal

> Мои вопросы чуть выше всё ещё в силе.

Ответы на эти вопросы можно получить в мане или немного попрактиковавшись. Будет лучше если ты сам их найдёшь.

Jini ★★
()
Ответ на: комментарий от Jini

Всё же на вопрос насчёт концов строк в регулярных выражениях
хотелось бы получить хоть какой-то ответ.
Не представляю, в каком-растаком man'е можно найти на это ответ.
Это вопрос "понятийного" характера.
Самостоятельно ответы на подобные вопросы можно искать годами.
Имеется в виду вот это:

Символ перевода строки с точки зрения sed
(и с точки зрения других программ, работающих "построчно")
является ли _составной_частью_ этой строки?
Если да, то где он находится с точки зрения регулярных выражений?

Например, ни одно из следующих выражений вроде не удаляет никакие пустые строки:
/^\n$/D
/\n^$/D
/^$\n/D
Почему?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от The_gray_Cardinal

Или по-другому:
чем отличается \n от $ в регулярных выражениях?
Во всех манах-растаманах сказано только, что
первое - это перевод строки,
а второе - это конец строки.
Можно объяснить словесно, чем одно отличается от другого?

The_gray_Cardinal
() автор топика
Ответ на: комментарий от The_gray_Cardinal

> Символ перевода строки с точки зрения sed (и с точки зрения других программ, работающих "построчно") является ли _составной_частью_ этой строки?

И да, и нет. При считывании файла sed обрезает символ перевода строки в конце перед помещением её в буфер. А при выполнении оператора N он вставляет этот символ между тем, что уже есть в буфере, и следующей прочитанной строкой. Когда символ \n уже есть в буфере, он обрабатывается как обычный символ. Другие программы могут проводить предварительную обработку входного потока по-другому, например, перл по умолчанию оставляет символ перевода строки.

> /^\n$/D

> /\n^$/D

> /^$\n/D


В этих трёх выражениях спецсимволы имеют разный смысл. Во втором ^ --- это обычный символ ^, а не начало буфера. В третьем $ теряет своё специальное значеие.

> чем отличается \n от $ в регулярных выражениях? Во всех манах-растаманах сказано только, что первое - это перевод строки, а второе - это конец строки.


Именно этим и отличаются. $ всегда указывает на конец буфера, а \n --- это рядовой символ, который трактуется по-особенному лишь командами D и P, но не регулярными выражениями.

Jini ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.