Дедупликация текстового файла и сохранение последнего вхождения в одном выходном файле и перемещение других в другой выходной файл

У меня есть файл с записями дупс (дупс в столбцах). Я хочу сохранить только последнее вхождение записей dup в файл и переместить все остальные дубли в другой файл.

Файл: вход

foo j
bar bn
bar b
bar bn
bar bn
bar bn
kkk hh
fjk ff
foo jj
xxx tt
kkk hh

Я использовал следующее выражение awk, чтобы сохранить последнее вхождение:

awk '{line=$0; x[$1]=line;} END{ for (key in x) print x[key];}' input > output

Файл: вывод

foo jj
xxx tt
fjk ff
kkk hh
bar bn

Как я могу переместить повторяющиеся записи в другой файл (оставив последнее вхождение)?

перемещение foo j в одном файле скажем d_output и сохраняя foo jj в выходном файле

3 ответа

Решение

Другой вариант, который вы можете попробовать, сохранить порядок, прочитав входной файл дважды:

awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=dups file file

выход:

bar bn
fjk ff
foo jj
xxx tt
kkk hh

Дубликаты:

$ cat dups
foo j
bar bn
bar b
bar bn
bar bn
kkk hh

@Sudo_O @WilliamPursell @ user2018441. Sudo_O спасибо за тест производительности. Я пытался воспроизвести их на моей системе, но это не имеет tac Доступно, поэтому я проверил с версией Кента и моей, но я не мог воспроизвести эти различия в моей системе.

Обновление: я тестировал версию Sudo_O, используя cat вместо tac, Хотя в системе с tac была разница в 0,2 секунды между tac а также cat при выводе в /dev/null (см. внизу этого поста)

Я получил:

Sudo_O
$ time cat <(seq 1 1000000) | awk 'a[$1]++{print $0 > "/dev/null";next}{print $0 > "/dev/null"}'

real    0m1.491s
user    0m1.307s
sys     0m0.415s

kent
$ time awk '$1 in a{print a[$1]>"/dev/null"}{a[$1]=$0}END{for(x in a)print a[x]}' <(seq 1 1000000) > /dev/null

real    0m1.238s
user    0m1.421s
sys     0m0.038s

scrutinizer
$ time awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=/dev/null <(seq 1 1000000) <(seq 1 1000000) > /dev/null

real    0m1.422s
user    0m1.778s
sys     0m0.078s

-

при использовании файла вместо seq Я получил:

Sudo_O
$ time cat <infile | awk 'a[$1]++{print $0 > "/dev/null";next}{print $0 > "/dev/null"}'

real    0m1.519s
user    0m1.148s
sys     0m0.372s


kent
$ time awk '$1 in a{print a[$1]>"/dev/null"}{a[$1]=$0}END{for(x in a)print a[x]}' <infile > /dev/null

real    0m1.267s
user    0m1.227s
sys     0m0.037s

scrutinizer
$ time awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=/dev/null <infile <infile > /dev/null

real    0m0.737s
user    0m0.707s
sys     0m0.025s

Вероятно, из-за эффектов кэширования, которые будут присутствовать и для более крупных файлов. Создание инфиля заняло:

$ time seq 1 1000000 > infile

real    0m0.224s
user    0m0.213s
sys     0m0.010s

Протестировано на другой системе:

$ time cat <(seq 1 1000000) > /dev/null

real    0m0.764s
user    0m0.719s
sys     0m0.031s
$ time tac <(seq 1 1000000) > /dev/null

real    0m1.011s
user    0m0.820s
sys     0m0.082s

Инструменты как tac а также rev милые!. Однако они не являются стандартными для всех дистрибутивов, особенно я обнаружил, что вы пометили вопрос unix, Также tac изменяет порядок вывода / dup.txt; если порядок должен быть сохранен, требуются дополнительные усилия для его поддержания.

Попробуйте эту строку:

awk '$1 in a{print a[$1]>"dup.txt"}{a[$1]=$0}END{for(x in a)print a[x]}' file

с вашим примером:

kent$  awk '$1 in a{print a[$1]>"dup.txt"}{a[$1]=$0}END{for(x in a)print a[x]}' file
foo jj
xxx tt
fjk ff
kkk hh
bar bn

kent$  cat dup.txt 
bar bn
bar b
bar bn
bar bn
foo j
kkk hh

Хитрость заключается в том, чтобы использовать tac сначала перевернуть файл (проще получить первое совпадение, чем последнее):

$ tac file | awk 'a[$1]++{print $0 > "dup";next}{print $0 > "output"}'

$ cat output
kkk hh
xxx tt
foo jj
fjk ff
bar bn

$ cat dup
kkk hh
bar bn
bar bn
bar b
bar bn
foo j

Редактировать:

Вот контрольные цифры для трех текущих решений на миллион линий:

sudo_o

real    0m2.156s
user    0m1.004s
sys     0m0.117s

kent

real    0m2.806s
user    0m2.718s
sys     0m0.080s

scrutinizer

real    0m4.033s
user    0m3.939s
sys     0m0.082s

Проверьте здесь http://ideone.com/IBrNeh

На моей локальной машине используется файл seq 1 1000000 > bench:

# sudo_o
$ time tac bench | awk 'a[$1]++{print $0 > "dup";next}{print $0 > "output"}' 

real    0m0.729s
user    0m0.668s
sys     0m0.101s

# scrutinizer
$ time awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=dups bench bench > output

real    0m1.093s
user    0m1.016s
sys     0m0.070s

# kent 
$ time awk '$1 in a{print a[$1]>"dup.txt"}{a[$1]=$0}END{for(x in a)print a[x]}' bench > output

real    0m1.141s
user    0m1.055s
sys     0m0.080s
Другие вопросы по тегам