Як видалити повторювані рядки у файлі, не сортуючи його в Unix?


137

Чи є спосіб видалити повторювані рядки з файлу в Unix?

Я можу зробити це з sort -uі uniqкоманд, але я хочу використовувати sedабо awk. Це можливо?


12
якщо ви маєте на увазі послідовні дублікати, то uniqодного достатньо.
Майкл Крелін - хакер

і в іншому випадку, я вважаю, що це можливо awk, але для великих файлів буде витрачено багато ресурсів.
Майкл Крелін - хакер

Дублікати stackoverflow.com/q/24324350 та stackoverflow.com/q/11532157 мають цікаві відповіді, які в ідеалі слід перенести сюди.
tripleee

Відповіді:


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

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


5
Щоб зберегти його у файлі, ми можемо це зробитиawk '!seen[$0]++' merge_all.txt > output.txt
Акаш Кандпал,

5
Тут важливий застереження: якщо вам потрібно зробити це для декількох файлів, і ви вставте більше файлів у кінці команди, або використовуйте підстановку… "видимий" масив заповниться повторюваними рядками ВСІХ файлів. Якщо ви хочете обробити кожен файл самостійно, вам потрібно зробити щось на кшталтfor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9

@ NickK9, що декупаж накопичується через декілька файлів, сам по собі є приголомшливим. Приємна порада
sfscs

31

З 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'

geekery ;-) +1, але споживання ресурсів неминуче.
Майкл Крелін - хакер

3
'$! N; /^(.*)\n\1$/!P; D 'означає "Якщо ви не в останньому рядку, читайте в іншому рядку. Тепер подивіться, що у вас є, і якщо це НЕ ТАКІ речі, після яких слід новий рядок, а потім знову ті самі речі, роздрукуйте матеріал. Тепер видаліть матеріал (до нового рядка). "
Бета-версія

2
'G; s / \ n / && /; / ^ ([- ~] * \ n). \ \ n \ 1 / d; s / \ n //; год; P 'означає, приблизно, "Додайте весь простір утримування до цього рядка, тоді, якщо ви побачите, що дублюється рядок, викиньте всю річ, інакше скопіюйте весь безлад назад у простір утримування та надрукуйте першу частину (що є саме рядком читати ".
Бета-версія

Чи $!потрібна частина? Не sed 'N; /^\(.*\)\n\1$/!P; D'робить те саме? Я не можу придумати приклад, коли на моїй машині два різні (fwiw, я намагався в кінці порожнього рядка з обома версіями, і вони обидва чудово).
Едді

1
Майже через 7 років, і ніхто не відповів на @amichair ... <sniff> робить мене сумним. ;) У будь-якому випадку, [ -~]представляє діапазон символів ASCII від 0x20 (пробіл) до 0x7E (тильда). Вони розглядаються в друкуються символи ASCII (пов'язана сторінка також 0x7F / видалення , але це не здається правильним). Це робить рішення порушеним для тих, хто не використовує ASCII, або хтось, хто використовує, скажімо, символи вкладки .. Більш портативний [^\n]включає насправді набагато більше символів ... насправді всі вони, крім одного.
B Layer

14

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

6

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

Нескінченний цикл, якщо останній рядок порожній і не має символів :

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

Не висить, але ви втрачаєте останній рядок

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

Пояснення знаходиться в самому кінці седу FAQ :

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

Для перетворення сценаріїв, які використовували колишню поведінку 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


4

Перше рішення також з 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.

Пояснює:

  1. $!N;: якщо поточний рядок НЕ останній рядок, використовуйте Nкоманду для читання наступного рядка в pattern space.
  2. /^(.*)\n\1$/!P: якщо вміст струму розділено pattern spaceдвома , що означає, що наступний рядок є поточним рядком, ми НЕ можемо роздрукувати його відповідно до нашої основної ідеї; в іншому випадку, що означає, що поточний рядок є ОСТАННІМ появою всіх його повторюваних послідовних рядків, тепер ми можемо використовувати команду для друку символів у поточній утиліті ( також надрукованій).duplicate string\nsamePpattern space\n\n
  3. D: ми використовуємо Dкоманду для видалення символів у поточній pattern spaceутиліті \n( \nтакож видаленій), тоді вміст pattern spaceнаступного рядка.
  4. і 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.

Пояснює:

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

та сама команда в Windows із зайнятим ящиком:busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
scavenger

-1

Цього можна досягти, використовуючи awk.
Нижче рядок відображатиме унікальні значення

awk file_name | uniq

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

awk file_name | uniq > uniq_file_name

новий файл uniq_file_name буде містити лише унікальні значення, без дублікатів


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

Видаляє повторювані рядки за допомогою awk.


1
Це порушить порядок ліній.
Віджай

1
Що таке текстовий файл 20 Гб? Занадто повільно.
Олександр Лубягін

Як завжди, марно. У будь-якому випадку, це вже робиться само собою, і не потрібно, щоб введення було рівно одним словом на рядок. catuniq
tripleee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.