Удалить дубликаты строк без сортировки

У меня есть служебный скрипт на 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;
}'
Другие вопросы по тегам