Есть ли способ удалить повторяющиеся строки в файле в 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 labelnamed 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.
catuniq
uniq, достаточно одного.