Удалите повторяющиеся строки, сохраняя порядок строк

[root@server]# awk '!seen[$0]++' out.txt > cleaned awk: (FILENAME=out.txt FNR=8547098) fatal error: internal error Aborted [root@server]# 

«Сервер» имеет: 8 ГБ ОЗУ + 16 Гбайт SWAP, x> 300 ГБ свободного места, amd64, настольный процессор. Scientific Linux 6.6. На нем ничего не работает, чтобы сделать LOAD. Awk прерывается через несколько секунд. Out.txt ~ 1,6 ГБ. GNU Awk 3.1.7.

Вопрос : Как удалить дубликаты строк, сохраняя порядок строк? Случай тоже важен, например: «А» и «а» – две разные линии, их нужно сохранить. Но «а» и «а» повторяются, требуется только первая.

Ответ может быть во всяком случае .. если awk не подходит для этого .. то perl / sed .. что может быть проблема?

 [root@server]# ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 61945 max locked memory (kbytes, -l) 99999999 max memory size (kbytes, -m) unlimited open files (-n) 999999 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 99999999 cpu time (seconds, -t) unlimited max user processes (-u) 61945 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited [root@server]# 

Обновление: я пробовал это на машине RHEL, он не прерывается, но у меня не было времени дождаться, когда это закончится. Почему Linux не отличается от RHEL?

Обновление: я пытаюсь использовать виртуальные Ubuntu 14, пока это работает! Это не проблема ulimit: mawk 1.3.3

 root@asdf-VirtualBox:~# ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 51331 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 51331 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited root@asdf-VirtualBox:~# 

5 Solutions collect form web for “Удалите повторяющиеся строки, сохраняя порядок строк”

Я сомневаюсь, что это будет иметь значение, но, на всякий случай, вот как сделать то же самое в Perl:

 perl -ne 'print if ++$k{$_}==1' out.txt 

Если проблема заключается в сохранении уникальных строк в памяти, это будет иметь ту же проблему, что и awk . Таким образом, другой подход может быть:

 cat -n out.txt | sort -k2 -k1n | uniq -f1 | sort -nk1,1 | cut -f2- 

Как это работает:

  1. В системе GNU cat -n добавит номер строки в каждую строку после некоторого количества пробелов, а затем символ <tab> . cat передает это входное представление для sort .

  2. sort -k2 указывает, что он должен учитывать символы из второго поля до конца строки при сортировке и sort поля по умолчанию по умолчанию в белых пробелах (или вставляемые пробелы и <tab> ) .
    Когда за ним следует -k1n , sort сначала рассматривает второе поле, а затем во-вторых – в случае идентичных полей -k2 – он рассматривает 1-е поле, но как отсортировано численно. Поэтому повторяющиеся строки будут отсортированы вместе, но в том порядке, в котором они появились.

  3. Результаты передаются по uniq которому предлагается игнорировать первое поле ( -f1 – а также как разделенное пробелом) – и это приводит к списку уникальных строк в исходном файле и возвращается обратно к sort .
  4. В этот раз sort сортируется по первому полю (номер вставленной линии cat ) численно, возвращая порядок сортировки к тому, что он был в исходном файле, и протрубит эти результаты.
  5. Наконец, cut удаляет номера строк, которые были вставлены cat . Это осуществляется путем cut печати только со второго поля через конец строки (а разделитель по умолчанию – символ <tab> ) .

Проиллюстрировать:

 $ cat file bb aa bb dd cc dd aa bb cc $ cat -n file | sort -k2 | uniq -f1 | sort -k1 | cut -f2- bb aa dd cc 
 #!/usr/bin/perl use DB_File; tie %h, 'DB_File'; while(<>){ not $h{$_} and print and $h{$_}=1 } 

EDIT 1: Действительно ли это работает? (Сравнение)

 Sol1 : Terdon et all Schwartzian-transform-like one-liner cat -n _1 | sort -uk2 | sort -nk1 | cut -f2- Sol2 : perl + DB_File (this answer) perl dbfile-uniq _1 Sol3 : PO (John W. Gill solution has a similar behavior) awk '!seen[$0]++' _1 Sol4: Terdon perl perl -ne 'print if ++$k{$_}==1' _1 

Случай 1 : 100_000_000 случайных чисел (по 5 цифр), 566 Мбайт, 31_212 различных значений:

 $ while true ; do echo $RANDOM; done | head -100000000 > _1 

Случай 2 : 50_000_000 номеров rand (10 цифр каждый), 516Mbytes, 48_351_464 различных значений:

 $ shuf _1 | sed 'N;s/\n/ /' > _11 

(следующий номер не очень надежный):

 ┌───────────────┬──────────┬───────────────┬────────┬──────────┐ │ │ Sol1 │ Sol2 │ Sol3 │ Sol4 │ │ │ sort... │ perl DB │ awk │ perl │ ├───────────────┼──────────┼───────────────┼────────┼──────────┤ │ case 1 │ 6m15 │ 6m17 │ 0m28 │ 0m28 │ ├───────────────┼──────────┼───────────────┼────────┴──────────┤ │ case 2 │ 11m15 │81m44 │ out of memory │ ├───────────────┼──────────┼───────────────┼────────┬──────────┤ │ case 2 │ │ 5m54 /cache=2G│ │ │ └───────────────┴──────────┴───────────────┴────────┴──────────┘ 

sol2 с кешем:

 use DB_File; use Fcntl ; $DB_HASH->{'cachesize'} = 2000_000_000; tie %h, 'DB_File', "_my.db", O_RDWR|O_CREAT|O_TRUNC, 0640, $DB_HASH; while(<>){ not $h{$_} and print and $h{$_}=1 } 

Сортировка также может быть оптимизирована с добавлением опции кэширования (не выполняется).

Один быстрый вывод:

  • sort – фантастическая команда!

Я использовал

 awk -v BINMODE=rw '!($0 in a){a[$0];print}' infile >> outfile 

BINMODE = rw: сохранить окончания терминаторов линии. (Я живу в смешанной среде os)

Логика проста.

Если текущая строка не находится в ассоциативном массиве, добавьте ее в ассоциативный массив и распечатайте для вывода.

При таком подходе могут быть ограничения памяти. Для очень больших файлов и наборов файлов я использовал варианты этого, используя хранилище файлов, чтобы преодолеть ограничения.

Сохраняющая порядок семантика вашей проблемы обладает чудесным свойством: вы можете разделить проблему. Вы можете сделать split -l 1000000 во входном файле; 1000000 строк, которые он производит, имеют лексически упорядоченные имена, которые хороши; затем унифицировать куски; и затем (в качестве второго прохода) унифицируют выходы этих.

Это решает проблему нехватки памяти (путем ограничения потребности в памяти) за счет превращения ее в многопроходное решение.

В частности:

Создание входных данных:

 $ cat make-uniqm-input.py #!/usr/bin/env python import random n = 1000000 for i in xrange(0, n): print random.randint(1000, 2000) $ python make-uniqm-input.py > uniqm-input.txt $ wc -l uniqm-input.txt 1000000 uniqm-input.txt 

Разделите входные данные:

 $ split -l 10000 uniqm-input.txt $ ls x?? | head xaa xab xac xad xae xaf xag xah xai xaj $ ls x?? | wc -l 100 $ cat x?? | wc -l 1000000 

Запустите uniqifier все сразу (сохраняет все уникальные строки ввода в памяти):

 # 'uniqm' is any order-preserving uniq implementation, such as # gawk '!counts[$0]++'. $ uniqm < uniqm-input.txt > output-no-splitting.txt $ wc -l output-no-splitting.txt 1001 output-no-splitting.txt 

Запустите uniqifier на разделенных кусках (сохраняет только уникальные строки ввода из каждой части в памяти), затем уменьшаем как второй проход:

 $ for x in x??; do uniqm < $x; done | uniqm > output-with-splitting.txt $ wc -l output-with-splitting.txt 1001 output-with-splitting.txt 

Для сравнения:

 $ diff output-no-splitting.txt output-with-splitting.txt $ head uniqm-input.txt 1506 1054 1623 1002 1173 1400 1226 1340 1824 1091 $ head output-with-splitting.txt 1506 1054 1623 1002 1173 1400 1226 1340 1824 1091 

Я не знаю отношение уникальных к неисторическим линиям в вашем входе, а также то, как хорошо смешиваются входные строки – так что есть какая-то настройка для определения количества разделяемых файлов, которые вам нужны.

Другой подход (стоит опубликовать в виде отдельного ответа): вместо подхода с разделенным файлом, который создает временные файлы, выполняет пакетную обработку в самом программном обеспечении uniqifier. Например, используя реализацию Ruby uniqifier для пояснения:

 require 'set' line_batch_count = 50000 # tunable parameter lines_seen = Set.new line_number = 0 ARGF.each do |line| line_number += 1 if (line_number % line_batch_count) == 0 lines_seen.clear end unless lines_seen.include? line puts line lines_seen << line end end 

Идея состоит в том, чтобы очистить хэш-набор так часто. Тогда это становится итеративным:

 $ cat uniqm-input.txt | ruby uniqm-capped.rb | wc -l 20021 $ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | wc -l 1001 $ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | head 1506 1054 1623 1002 1173 1400 1226 1340 1824 1091 

Таким образом, вы можете запускать эту ограниченную версию повторно, пока счетчик строк не изменится с одной итерации на другую.

Обратите внимание, что этот метод capped-uniqm не зависит от языка: вы можете очистить массив lines_seen каждые N строк, независимо от того, используете ли вы awk, python, perl, C ++ и т. Д. Для всех этих языков установлены понятные методы; Я считаю, что delete awk является нестандартным, но распространенным.

  • поместите вкладку перед каждой выходной строкой на AIX / ksh
  • Заставить sed запрашивать подтверждение перед каждой заменой?
  • как добавить дополнительный символ после поиска слова
  • разделить большой файл на новый файл с уникальными именами файлов
  • Печать строки после n-го появления совпадения
  • Удалить до заданного символа, начиная с заданной строки
  • Передача обычных файлов только на `sed -i`
  • Grepping для блока текста с частями, которые могут быть необязательными
  • Прочитайте строку из файла и добавьте ее в ту же строку другого файла
  • Подсчитать каждую строку поиска выходных awk в файле
  • Как добавить заголовок и / или нижний колонтитул в поток sed или awk?
  • Linux и Unix - лучшая ОС в мире.