У меня есть файл, который выглядит как этот пример игрушки. В моем текущем файле 4 миллиона строк, около 10 из которых мне нужно удалить.
ID Data1 Data2
1 100 100
2 100 200
3 200 100
ID Data1 Data2
4 100 100
ID Data1 Data2
5 200 200
Я хочу удалить строки, которые выглядят как заголовок, за исключением первой строки.
Конечный файл:
ID Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200
Как я могу это сделать?
text-processing
Гай Август
источник
источник
{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
head -1
был устаревшим на протяжении десятилетий до этого.Ты можешь использовать
Это удалит строки с идентификатором, начиная со строки 2.
источник
sed '2,${/^ID Data1 Data2$/d;}' file
(конечно , используя правильное количество пробелов между столбцами)sed
смысла, нет.sed '1!{/ID/d;}'
Для тех, кто не любит фигурные скобки
n
означает номерpass
строки1
d
удалить все совпадающие строки, которые начинаются с^ID
источник
sed '1n;/^ID/d'
имени файла. просто предложениеIDfoo
которым не совпадают с заголовком (в этом случае вряд ли что-то изменится , но вы никогда не узнаете).Вот веселый Вы можете использовать
sed
непосредственно для удаления всех копий первой строки и оставить все остальное на месте (включая саму первую строку).1{h;n;}
помещает первую строку в область удержания, печатает ее и читает следующую строку, пропуская остальныеsed
команды для первой строки. (Он также пропускает этот первый1
тест для второй строки , но это не имеет значения, поскольку этот тест не был бы применен ко второй строке.)G
добавляет новую строку, за которой следует содержимое области удержания, в пространство шаблона./^\(.*\)\n\1$/d
удаляет содержимое пространства шаблона (таким образом пропуская к следующей строке), если часть после новой строки (то есть то, что было добавлено из области удержания) точно совпадает с частью перед новой строкой. Здесь строки, которые дублируют заголовок, будут удалены.s/\n.*$//
удаляет часть текста, которая была добавленаG
командой, так что распечатывается только строка текста из файла.Однако, поскольку регулярное выражение является дорогостоящим, несколько более быстрый подход состоял бы в том, чтобы использовать то же условие (отрицание) и
P
повторную печать до новой строки, если часть после новой строки (то есть то, что было добавлено из пространства удержания) не совсем соответствует части перед новой строкой, а затем безоговорочно удалите пространство шаблона:Вывод, если дан ваш ввод:
источник
sed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input
; как-то мне легче читать. :)Вот еще пара вариантов, которые не требуют, чтобы вы знали первую строку заранее:
-n
Флаг говорит Perl для цикла по его входному файлу, сохраняя каждую строку как$_
.$k=$_ if $.==1;
Сохраняет первую линию ($.
номер строки, так$.==1
будет верно только для 1 - й линии) , как$k
. Вprint unless $k eq $_
печатает текущую строку , если это не то же самое , как один спасен в$k
.В качестве альтернативы, то же самое в
awk
:Здесь мы проверяем, совпадает ли текущая строка с той, что сохранена в переменной
x
. Если тест$0!=x
оценивается как true (если текущая строка$0
не совпадает сx
), строка будет напечатана, потому что действие по умолчанию для awk в выражениях true - это печать. Первая строка (NR==1
) сохраняется какx
. Поскольку это делается после проверки соответствия текущей строкиx
, это гарантирует, что первая строка также будет напечатана.источник
!($0 in a)
тестирует без создания и избегает этого, или awk может выполнить ту же логику, что и для perl:'$0!=x; NR==1{x=$0}'
или если строка заголовка может быть пустой'NR==1{x=$0;print} $0!=x'
!a[$0]
? Зачем это создает запись вa
?AWK - вполне приличный инструмент для этой цели. Вот пример запуска кода:
Сломать :
NR == 1 {print}
говорит нам напечатать первую строку текстового файлаNR != 1 && $0!~/ID Data1 Data2/
Логический оператор&&
указывает AWK напечатать строку, которая не равна 1 и не содержитID Data1 Data2
. Обратите внимание на отсутствие{print}
части; в awk, если тестовое условие оценивается как true, предполагается, что строка должна быть напечатана.| head -n 10
это всего лишь небольшое дополнение, ограничивающее вывод только первыми 10 строками. Не относится к самойAWK
части, используется только для демонстрационных целей.Если вы хотите это в файле, перенаправьте вывод команды, добавив
> newFile.txt
в конце команды, например, так:Как это держится? Довольно неплохо на самом деле:
Примечание
Сгенерированный пример файла был сделан для того, чтобы выполнить цикл от одного до миллиона и напечатать первые четыре строки вашего файла (таким образом, 4 строки на миллион равны 4 миллионам строк), что, кстати, заняло 0,09 секунды.
источник
ID Data1 Data2 foo
которым не совпадают с заголовком (в этом случае вряд ли что-то изменится , но вы никогда не узнаете).Awk, адаптируясь к любому заголовку автоматически:
то есть, в первой строке получите заголовок и напечатайте его, и последующая строка, ОТЛИЧНАЯ от этого заголовка, будет напечатана.
FNR = количество записей в текущем файле, так что вы можете иметь несколько файлов, и он будет делать то же самое в каждом из них.
источник
Для полноты Perl решение IMO немного более элегантно, чем @terdon:
источник
ID
. У вас нет гарантии, что это не удалит строки, которые должны быть сохранены. Так как вы воспитали элегантность,g
бессмысленно использовать^
и$
. На самом деле, все ваши вариантыm///
здесь бесполезны, кромеs
; они активируют функции, которые вы не используете. Так что$
,s/^ID.*//s
сделал бы то же самое.Просто немного отодвинем вопрос ... похоже, что ваш вклад сам по себе является результатом объединения нескольких файлов TSV. Если вы можете выполнить резервное копирование шага в конвейере обработки (если вы владеете этим или можете общаться с теми, кто это делает), вы можете использовать инструмент с поддержкой заголовков, чтобы объединить данные в первую очередь и тем самым устранить проблему необходимости удалить лишние строки заголовка.
Например, используя Миллер :
источник