Чи є спосіб видалити повторювані рядки з файлу в Unix?
Я можу зробити це з sort -u
і uniq
команд, але я хочу використовувати sed
або awk
. Це можливо?
awk
, але для великих файлів буде витрачено багато ресурсів.
Чи є спосіб видалити повторювані рядки з файлу в Unix?
Я можу зробити це з sort -u
і uniq
команд, але я хочу використовувати sed
або awk
. Це можливо?
awk
, але для великих файлів буде витрачено багато ресурсів.
Відповіді:
awk '!seen[$0]++' file.txt
seen
це асоціативний масив, до якого Awk передасть кожен рядок файлу. Якщо рядок відсутній у масиві, то seen[$0]
він оцінить значення false. Це !
логічний оператор НЕ і перетворить помилкове в істинне. Awk надрукує рядки, в яких вираз оцінюється на істинне. У ++
прирости seen
так , що seen[$0] == 1
після того, як в перший раз рядок знайдена , а потім seen[$0] == 2
, і так далі.
Awk оцінює все, окрім 0
та ""
(порожній рядок) до true. Якщо дублікат буде розміщений у, seen
тоді він !seen[$0]
буде оцінюватись як хибний, а рядок не буде записаний у висновок.
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
З 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'
$!
потрібна частина? Не sed 'N; /^\(.*\)\n\1$/!P; D'
робить те саме? Я не можу придумати приклад, коли на моїй машині два різні (fwiw, я намагався в кінці порожнього рядка з обома версіями, і вони обидва чудово).
[ -~]
представляє діапазон символів ASCII від 0x20 (пробіл) до 0x7E (тильда). Вони розглядаються в друкуються символи ASCII (пов'язана сторінка також 0x7F / видалення , але це не здається правильним). Це робить рішення порушеним для тих, хто не використовує ASCII, або хтось, хто використовує, скажімо, символи вкладки .. Більш портативний [^\n]
включає насправді набагато більше символів ... насправді всі вони, крім одного.
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\1$/!P; D'
Не висить, але ви втрачаєте останній рядок
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Пояснення знаходиться в самому кінці седу FAQ :
Підтримувач GNU sed вважав, що, незважаючи на проблеми з портативністю,
це може спричинити, зміна команди N для друку (а не
видалення) простір шаблону більше відповідає інтуїції
про те, як повинна вести себе команда "додавати наступний рядок" .
Ще один факт, що сприяє зміні, полягав у тому, що "{N; команда;}"
видалить останній рядок, якщо файл має непарне число рядків, але
надрукує останній рядок, якщо файл має парну кількість рядків.Для перетворення сценаріїв, які використовували колишню поведінку N (видалення
простору шаблону при досягненні EOF), до скриптів, сумісних з
усіма версіями sed, змінити одинокий "N;" до "$ d; N;" .
$ 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
\n
\n
D
: ми використовуємо D
команду для видалення символів у поточній pattern space
утиліті \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
команду set label
назви loop
.N
для читання наступного рядка в pattern space
.s/^(.*)\n\1$/\1/
для видалення поточного рядка, якщо наступний рядок збігається з поточним рядком, ми використовуємо s
команду для виконання delete
дії.s
команда виконана успішно, то використовуйте tloop
командну силу, sed
щоб перейти до label
названого loop
, що зробить той самий цикл до наступних рядків утиліту, немає жодних повторюваних послідовних рядків рядка, який є latest printed
; в іншому випадку використовуйте D
команду до delete
рядка, який збігається з latest-printed line
, і примушуйте sed
перейти до першої команди, яка є p
командою, зміст поточного pattern space
- наступний новий рядок.busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Видаляє повторювані рядки за допомогою awk.
uniq
одного достатньо.