Как я могу удалить дубликаты строк в файле в 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.bak
perl -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
следующая новая линия.