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


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

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

8 81

8 ответов:

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

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

от 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$/!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/d; s/\n//; h; P'

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

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

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

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

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

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

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

объяснение находится в самом конце sed FAQ:

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

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

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

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

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

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

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

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

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n$/!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.

объясняю:

  1. $!N;: если текущая строка не является последней строкой, используйте N команда для чтения следующей строки в pattern space.
  2. /^(.*)\n$/!P: если содержимое текущего pattern space два duplicate string разделены \n, что означает, что следующая строка является same С текущей мы не можем напечатать его в соответствии с нашей основной идеей; в противном случае, что означает, что текущая строка является последним появлением всех ее повторяющихся последовательных строк, теперь мы можем использовать P команда для печати символов в текущем pattern space полезное \n (\n также напечатано).
  3. D: мы используем D команда для удаления символов в текущем pattern space полезное \n (\n также удалены), то содержание pattern space следующая линия.
  4. и будет сила sed, чтобы перейти к его FIRST команда $!N, но не читать следующую строку из файла или стандартного входного потока.

второе решение легко понять (от себя):

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n$//;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.

объясняю:

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

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

awk file_name | uniq

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

awk file_name | uniq > uniq_file_name

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

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

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