Как удалить вторую строку последовательных строк, начинающихся с одного и того же слова?
у меня есть текстовый файл с взаимозаменяемыми строками, начинающимися с "TITLE" и "DATA", но иногда есть повторяющиеся строки, начинающиеся с "TITLE":
название что-то
Данные некоторые данные
Название-то другое
Данные некоторые другие данные
Название еще немного
Название дополнительная информация
Данные еще некоторые данные
Я хотел бы иметь возможность обнаруживать повторяющиеся строки, начинающиеся с "TITLE", и сохранять только первую строку каждая такая пара.
Я понял, что регулярное выражение для захвата этих ^TITLE.*n^TITLE.*n
теперь я хотел бы включить это в однострочный perl
/bash
/sed
/awk
команда, которая удалит вторую строку и выведет остальную часть файла, но я не мог понять это.
5 ответов
решение Perl:
perl -ne 'print unless $t and /^TITLE/; $t = /^TITLE/'
Он помнит, была ли предыдущая строка заголовком в $t
переменной.
вот один из способов сделать это с GNU sed:
sed -r 'N; /(TITLE)[^\n]*\n/ s/\n.*//; P; D' infile
-
N
помещает вторую строку в пространство шаблона. - тест соответствия, если обе строки начинаются с
TITLE
. - если это так вторая линия удаляется.
-
P; D
печать и удаление первой строки в пространстве шаблонов.
выход:
TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
DATA some more data
Edit-обрабатывать произвольное количество повторений
как отметил Никитину Reklawyks в комментариях приведенное выше решение работает только с двумя последовательными строками, начинающимися с TITLE
, для обработки произвольного количества повторений, простой цикл может быть добавлен следующим образом:
sed -r ':a; N; /(TITLE)[^\n]*\n/ s/\n.*//; ta; P; D' infile
на ta
заявление делает sed перейти к :a
ярлык для s///
успешно.
другой способ сделать это - использовать С coreutils
, это не так гибко, но тем не менее хорошо работает в этом дело:
uniq -w5 infile
мне кажется, что у вас есть записи, которые состоят из двух полей, заголовка и данных, и что если вам не хватает второго поля, вы хотите удалить запись. Но это не то, что ты задал в своем вопросе. Так вот один из способов сделать то, что вы просили:
awk '/^TITLE/&&!t{t=} /^DATA/&&t{print t;print;t=""}' inputfile
идея здесь в том, что мы установим переменную в заголовок, когда увидим его, и еще не имеем набора заголовков, а затем только распечатаем его, когда увидим данные. Это работает для входных данных, которые вы предоставили, если я читаю ваши Вопрос Правильный. Выход есть:
TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
DATA some more data
как вы можете видеть, последняя строка заголовка в вашем наборе данных была удалена.
и вот еще один способ сделать это в awk...
awk '/^TITLE/&&t{next} t=0; /^TITLE/{t=1} 1' inputfile
в этом, первое выражение пропускает заголовки, если t
Бен поставил. Второе выражение расстраивает t
. Третье выражение задает if для заголовков, а последнее выражение (1
) печатает строку. Конечно, последние три выражения не запускаются, если мы пропустили строку в первое выражение. Он генерирует тот же результат, что и выше, и не утруждает себя просмотром /^DATA/
.
наконец, это наименьший код, но самая странная логика:
awk '/^DATA/ || !t; {t=/^TITLE/}' inputfile
он печатает все строки данных, или любой линии, где t
не установлен, а затем эффективно устанавливает t
к логическому, влияющему на оценку следующей строки. Если вы делаете это в csh или tcsh, остерегайтесь восклицательного знака, который в этих оболочках может потребоваться избежать.
попробуйте этот однострочный:
awk '/^TITLE/&&f{next;} {if (~/^TITLE/)f=1;else f=0}1' file
выход:
TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
DATA some more data