Дедупликация текстового файла и сохранение последнего вхождения в одном выходном файле и перемещение других в другой выходной файл
У меня есть файл с записями дупс (дупс в столбцах). Я хочу сохранить только последнее вхождение записей 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