Удалить дубликаты строк без сортировки
У меня есть служебный скрипт на Python:
#!/usr/bin/env python
import sys
unique_lines = []
duplicate_lines = []
for line in sys.stdin:
if line in unique_lines:
duplicate_lines.append(line)
else:
unique_lines.append(line)
sys.stdout.write(line)
# optionally do something with duplicate_lines
Эта простая функциональность (uniq без необходимости сначала сортировать, стабильное упорядочение) должна быть доступна в виде простой утилиты UNIX, не так ли? Может быть, комбинация фильтров в трубе?
Причина, по которой спрашивают: нужна эта функциональность в системе, в которой я не могу запустить python из любого места
8 ответов
Блог сценариев UNIX Bash предлагает:
awk '!x[$0]++'
Эта команда сообщает awk, какие строки печатать. Переменная $0
содержит все содержимое строки и квадратные скобки для доступа к массиву. Итак, для каждой строки файла, узел массива x
увеличивается, и строка печатается, если содержимое этого узла не было (!
) предварительно установлено.
Поздний ответ - я только что натолкнулся на дубликат этого - но, возможно, стоит добавить...
Принцип ответа @1_CR можно записать более кратко, используя cat -n
вместо awk
добавить номера строк:
cat -n file_name | sort -uk2 | sort -nk1 | cut -f2-
- использование
cat -n
предварять номера строк - использование
sort -u
удалить дубликаты данных - использование
sort -n
сортировать по предварительно указанному номеру - использование
cut
убрать нумерацию строк
Чтобы удалить дубликаты из 2 файлов:
awk '!a[$0]++' file1.csv file2.csv
Теперь вы можете проверить этот небольшой инструмент, написанный на Rust: uq.
Он выполняет фильтрацию уникальности без предварительной сортировки входных данных, поэтому может применяться к непрерывному потоку.
Решение Майкла Хоффмана, приведенное выше, короткое и приятное. Для больших файлов подход с преобразованием Шварца, включающий добавление индексного поля с использованием awk, за которым следуют несколько раундов сортировки и uniq, требует меньших затрат памяти. Следующий фрагмент работает в Bash
awk '{print(NR"\t"$0)}' file_name | sort -t$'\t' -k2,2 | uniq --skip-fields 1 | sort -k1,1 -t$'\t' | cut -f2 -d$'\t'
Спасибо 1_CR! Мне нужен был "uniq -u" (полностью удалить дубликаты), а не uniq (оставить 1 копию дубликатов). Решения awk и perl не могут быть изменены, чтобы сделать это, вы можете! Возможно, мне также понадобилось меньшее использование памяти, так как я буду уникален, как 100 000 000 строк 8-). На всякий случай, если кому-то еще это нужно, я просто помещаю "-u" в часть команды uniq:
awk '{print(NR"\t"$0)}' file_name | sort -t$'\t' -k2,2 | uniq -u --skip-fields 1 | sort -k1,1 -t$'\t' | cut -f2 -d$'\t'
uniq
команда работает под псевдонимом даже http://man7.org/linux/man-pages/man1/uniq.1.html
Я просто хотел удалить все дубликаты в следующих строках, а не везде в файле. Поэтому я использовал:
awk '{
if ($0 != PREVLINE) print $0;
PREVLINE=$0;
}'