Есть ли способ удалить повторяющиеся строки в файле в 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
и ""
(пустая строка), как истина. Если в него помещена повторяющаяся строка, seen
тогда !seen[$0]
будет вычислено значение false, и строка не будет записана на вывод.
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, аналогичный 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 по sed :
Сопровождающий GNU sed чувствовал, что, несмотря на проблемы с переносимостью, которые
это может вызвать, изменение команды N на печать (а не
удаление) пространства шаблонов больше соответствовало интуитивным представлениям
о том, как должна себя вести команда для «добавления следующей строки» .
Другой факт, благоприятствующий изменению, заключался в том, что "{N; command;}"
удалит последнюю строку, если в файле нечетное количество строк, но
напечатает последнюю строку, если в файле четное количество строк.Чтобы преобразовать сценарии, в которых использовалось прежнее поведение 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 a label
named loop
.N
для чтения следующей строки в pattern space
.s/^(.*)\n\1$/\1/
для удаления текущей строки, если следующая строка совпадает с текущей строкой, мы используем s
команду для выполнения delete
действия.s
команда выполнена успешно, то используйте tloop
команду force, 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"
Этого можно добиться с помощью awk.
Ниже линии будут отображаться уникальные значения.
awk file_name | uniq
Вы можете вывести эти уникальные значения в новый файл
awk file_name | uniq > uniq_file_name
новый файл uniq_file_name будет содержать только уникальные значения, без дубликатов
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Удаляет повторяющиеся строки с помощью awk.
cat
uniq
uniq
, достаточно одного.