Как удалить дубликаты строк в файле, не сортируя его в Unix?

Есть ли способ удалить дубликаты строк в файле в Unix?

Я могу сделать это с помощью команд sort -u и uniq, но я хочу использовать sed или awk. Это возможно?

Ответ 1

awk '!seen[$0]++' file.txt

seen является ассоциативным массивом, который Awk передает каждой строке файла. Если строка не находится в массиве, то seen[$0] будет оцениваться как false. ! является логическим оператором NOT и инвертирует значение false в true. Awk напечатает строки, в которых выражение будет равно true. ++ увеличивает seen так, что seen[$0] == 1 после первого появления строки, а затем seen[$0] == 2 и т.д.
Awk оценивает все, кроме 0 и "" (пустая строка), для true. Если повторяющаяся строка помещается в seen, то !seen[$0] будет оцениваться как false, и строка не будет записана на выход.

Ответ 2

От http://sed.sourceforge.net/sed1line.txt: (Пожалуйста, не спрашивайте меня, как это работает;-))

 # delete duplicate, consecutive lines from a file (emulates "uniq").
 # First line in a set of duplicate lines is kept, rest are deleted.
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # delete duplicate, nonconsecutive lines from a file. Beware not to
 # overflow the buffer size of the hold space, or else use GNU sed.
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

Ответ 3

Perl one-liner похож на решение @jonas awk:

perl -ne 'print if ! $x{$_}++' file

Этот вариант удаляет конечные пробелы перед сравнением:

perl -lne 's/\s*$//; print if ! $x{$_}++' file

Этот вариант редактирует файл на месте:

perl -i -ne 'print if ! $x{$_}++' file

Этот вариант редактирует файл на месте и создает резервную копию file.bak

perl -i.bak -ne 'print if ! $x{$_}++' file

Ответ 4

Однострочный шрифт, описанный Андре Миллером выше, за исключением последних версий sed, когда входной файл заканчивается пустой строкой и без символов. На моем Mac мой процессор просто вращается.

Бесконечный цикл, если последняя строка пуста и не имеет символов:

sed '$!N; /^\(.*\)\n\1$/!P; D'

Не зависает, но вы теряете последнюю строку

sed '$d;N; /^\(.*\)\n\1$/!P; D'

Объяснение находится в самом конце часто задаваемых вопросов:

Сторонник GNU sed счел, что, несмотря на проблемы с переносимостью    это приведет к изменению команды N для печати (а не    delete) пространство шаблонов было более согласовано с одной интуицией
   о том, как должна вести себя команда "добавить следующую строку".
   Другим фактом, благоприятным для изменения, было то, что "{N; command;}" будет    удалите последнюю строку, если файл имеет нечетное число строк, но
   напечатайте последнюю строку, если файл имеет четное количество строк.

Чтобы преобразовать скрипты, которые использовали прежнее поведение N (удаление    пространство шаблонов при достижении EOF) для скриптов, совместимых с    все версии sed, изменить одиночный "N"; до "$ d; N;" .

Ответ 5

Альтернативный способ использования Vim (Vi-совместимый):

Удалить повторяющиеся, последовательные строки из файла:

vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq

Удалить из файла дубликаты, несоответствия и непустые строки:

vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq

Ответ 6

Первое решение также относится к http://sed.sourceforge.net/sed1line.txt

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5

Основная идея:

print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.

Объясняет:

  • $!N;: если текущая строка НЕ ​​является последней строкой, используйте команду N, чтобы прочитать следующую строку в pattern space.
  • /^(.*)\n\1$/!P: если содержимое текущего pattern space равно двум duplicate string, разделенным на \n, что означает, что следующая строка - это same с текущей строкой, мы НЕ можем ее распечатать в соответствии с нашей основной идеей; в противном случае это означает, что текущая строка представляет собой ПОСЛЕДНЕЕ появление всех повторяющихся последовательных строк, теперь мы можем использовать команду P для печати символов в текущем pattern space util \n (\n также напечатано).
  • D: мы используем команду D для удаления символов в текущем pattern space util \n (\n также удаляется), тогда содержимое pattern space является следующей строкой. Команда
  • и D заставит sed перейти к своей команде FIRST $!N, но НЕ читать следующую строку из файла или стандартного потока ввода.

Второе решение легко понять (от меня):

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5

Основная идея:

print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.

Объясняет:

  • прочитайте новую строку из потока ввода или файла и напечатайте ее один раз.
  • используйте :loop набор команд a label с именем loop.
  • используйте N для чтения следующей строки в pattern space.
  • используйте s/^(.*)\n\1$/\1/ для удаления текущей строки, если следующая строка совпадает с текущей строкой, мы используем команду s для выполнения действия delete.
  • если команда s выполнена успешно, используйте команду tloop command force sed, чтобы перейти к label с именем loop, которая будет делать тот же цикл для следующих строк, если нет дубликатов последовательные линии линии, которая latest printed; в противном случае используйте команду D для delete строки, которая совпадает с линией latest-printed line, а force sed - перейти к первой команде, которая является командой P, содержимое текущего pattern space - следующая новая строка.

Ответ 7

Это может быть достигнуто с помощью awk
Внизу строки будут отображаться уникальные значения

awk file_name | uniq

Вы можете вывести эти уникальные значения в новый файл

awk file_name | uniq > uniq_file_name

новый файл uniq_file_name будет содержать только уникальные значения, без дубликатов

Ответ 8

cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'

Удаляет повторяющиеся строки с помощью awk.