Как удалить дубликаты строк в файле, не сортируя его в Unix?
Есть ли способ удалить дубликаты строк в файле в Unix?
Я могу сделать это с помощью команд sort -u
и uniq
, но я хочу использовать sed
или awk
.
Это возможно?
Ответы
Ответ 1
awk '!seen[$0]++' file.txt
seen
является ассоциативным массивом, который Awk передает каждой строке файла. Если строка не находится в массиве, то seen[$0]
будет оцениваться как false. !
является логическим оператором NOT и инвертирует значение false в true. Awk напечатает строки, в которых выражение будет равно true. ++
увеличивает seen
так, что seen[$0] == 1
после первого появления строки, а затем seen[$0] == 2
и т.д.
Awk оценивает все, кроме 0
и ""
(пустая строка), для true. Если повторяющаяся строка помещается в seen
, то !seen[$0]
будет оцениваться как false, и строка не будет записана на выход.
Ответ 2
От 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'
Ответ 3
Perl one-liner похож на решение @jonas awk:
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
Ответ 4
Однострочный шрифт, описанный Андре Миллером выше, за исключением последних версий sed, когда входной файл заканчивается пустой строкой и без символов. На моем Mac мой процессор просто вращается.
Бесконечный цикл, если последняя строка пуста и не имеет символов:
sed '$!N; /^\(.*\)\n\1$/!P; D'
Не зависает, но вы теряете последнюю строку
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Объяснение находится в самом конце часто задаваемых вопросов:
Сторонник GNU sed счел, что, несмотря на проблемы с переносимостью это приведет к изменению команды N для печати (а не delete) пространство шаблонов было более согласовано с одной интуицией
о том, как должна вести себя команда "добавить следующую строку".
Другим фактом, благоприятным для изменения, было то, что "{N; command;}" будет удалите последнюю строку, если файл имеет нечетное число строк, но
напечатайте последнюю строку, если файл имеет четное количество строк.
Чтобы преобразовать скрипты, которые использовали прежнее поведение 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
Ответ 6
$ 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
util \n
(\n
также напечатано).
-
D
: мы используем команду D
для удаления символов в текущем pattern space
util \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
набор команд a label
с именем loop
.
- используйте
N
для чтения следующей строки в pattern space
.
- используйте
s/^(.*)\n\1$/\1/
для удаления текущей строки, если следующая строка совпадает с текущей строкой, мы используем команду s
для выполнения действия delete
.
- если команда
s
выполнена успешно, используйте команду tloop
command force sed
, чтобы перейти к label
с именем loop
, которая будет делать тот же цикл для следующих строк, если нет дубликатов последовательные линии линии, которая latest printed
; в противном случае используйте команду D
для delete
строки, которая совпадает с линией latest-printed line
, а force sed
- перейти к первой команде, которая является командой P
, содержимое текущего pattern space
- следующая новая строка.
Ответ 7
Это может быть достигнуто с помощью awk
Внизу строки будут отображаться уникальные значения
awk file_name | uniq
Вы можете вывести эти уникальные значения в новый файл
awk file_name | uniq > uniq_file_name
новый файл uniq_file_name будет содержать только уникальные значения, без дубликатов
Ответ 8
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Удаляет повторяющиеся строки с помощью awk.