Как я могу удалить дубликаты строк в файле в Unix?
есть ли способ удалить дубликаты строк в файле в Unix?
Я могу сделать это с sort -u и uniq команды, но я хочу использовать sed или awk.
Это возможно?
8 ответов:
awk '!seen[]++' file.txt
seen- это ассоциативный массив, в который Awk будет передавать каждую строку файла. Если строка не находится в массиве, тоseen[]будет false. Элемент!- это логический оператор not и поменять false на True. Awk напечатает строки, в которых выражение принимает значение true. Элемент++С шагомseen, Так чтоseen[] == 1после первого раза строка найдена, а затемseen[] == 2и так далее.
Awk оценивает все, но0и""(пустая строка) к истине. Если дубликат строки помещается вseenзатем!seen[]будет false и строка не будет записана в выходной.
от 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$/!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/d; s/\n//; h; P'
Perl one-liner похож на решение awk @jonas:
perl -ne 'print if ! $x{$_}++' fileэтот вариант удаляет конечные пробелы перед сравнением:
perl -lne 's/\s*$//; print if ! $x{$_}++' fileэтот вариант редактирует файл на месте:
perl -i -ne 'print if ! $x{$_}++' fileэтот вариант редактирует файл на месте, и делает резервную
file.bakperl -i.bak -ne 'print if ! $x{$_}++' file
один лайнер, который Андре Миллер опубликовал выше, работает за исключением последних версий sed, когда входной файл заканчивается пустой строкой и без символов. На моем Mac мой процессор просто вращается.
бесконечный цикл, если последняя строка пустая и не имеет символа:
sed '$!N; /^\(.*\)\n$/!P; D'не зависает, но вы теряете последнюю строку
sed '$d;N; /^\(.*\)\n$/!P; D'объяснение находится в самом конце sed FAQ:
сопровождающий GNU sed чувствовал, что несмотря на проблемы с переносимостью
это приведет к изменению команды N для печати (а не
удалить) пространство шаблонов было более совместимо с интуицией
о том, как команда "добавить следующую строку"должно вести себя.
Еще один факт в пользу изменений стало то, что "{команда N;;}" будет
удалите последнюю строку, если файл имеет нечетное значение количество строк, но
выведите последнюю строку, если в файле четное число строк.для преобразования скриптов, которые использовали прежнее поведение N (удаление
пространство шаблонов при достижении EOF) для скриптов, совместимых с
все версии СЭД измените одинокий "N;" на " $d;N;".
альтернативный способ использования Vim (VI совместимый):
удалить повторяющиеся, последовательные строки из файла:
vim -esu NONE +'g/\v^(.*)\n$/d' +wqудалить повторяющиеся, несекретные и непустые строки из файла:
vim -esu NONE +'g/\v^(.+)$\_.{-}^$/d' +wq
первое решение также от http://sed.sourceforge.net/sed1line.txt
$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n$/!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$/!P: если содержимое текущегоpattern spaceдваduplicate stringразделены\n, что означает, что следующая строка являетсяsameС текущей мы не можем напечатать его в соответствии с нашей основной идеей; в противном случае, что означает, что текущая строка является последним появлением всех ее повторяющихся последовательных строк, теперь мы можем использоватьPкоманда для печати символов в текущемpattern spaceполезное\n(\nтакже напечатано).D: мы используемDкоманда для удаления символов в текущемpattern spaceполезное\n(\nтакже удалены), то содержаниеpattern spaceследующая линия.- и будет сила
sed, чтобы перейти к егоFIRSTкоманда$!N, но не читать следующую строку из файла или стандартного входного потока.второе решение легко понять (от себя):
$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n$//;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 alabelимениloop.- использовать
Nчтобы прочитать следующую строку вpattern space.- использовать
s/^(.*)\n$//чтобы удалить текущую строку, если следующая строка совпадает с текущей строкой, мы используемsкоманда для выполненияdeleteдействие.- если
sкоманда выполнена успешно, затем используйтеtloopкомандованияsedперейти кlabelимениloop, который будет делать тот же цикл для следующих строк util нет повторяющихся последовательных строк строки, которая являетсяlatest printed; в противном случае используйтеDкоманда наdeleteлиния, которая совпадает сlatest-printed lineи группаsedперейти к первой команде, которая являетсяpкоманда, содержание текущегоpattern spaceследующая новая линия.