[РЕШЕНО] Удалить повторяющиеся строки

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Ответить
Аватара пользователя
newsrc
Сообщения: 314
ОС: Slackware

[РЕШЕНО] Удалить повторяющиеся строки

Сообщение newsrc »

С помощью sed или иначе, как удалить повторяющиеся более двух раз строки из файла?
Система -- это совокупность элементов и связей, дающая новые свойства, не присущие ни одному из элементов и ни одной из связей.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение /dev/random »

newsrc писал(а):
08.06.2014 13:02
С помощью sed или иначе, как удалить повторяющиеся более двух раз строки из файла?

Если эти повторяющиеся строки всегда идут подряд, используйте uniq. Если нет - sort -u. Если во втором случае нужно, чтобы порядок строк сохранялся (sort, как ясно из имени, их отсортирует), то пронумеруйте строки, отсортируйте с удалением дублей без учёта номера (см. опцию -k), отсортируйте оставшиеся по номеру и удалите номера с помощью sed или cut.

PS: Варианты, что я перечислил, вычистят все повторы, а не только те строки, что повторяются более _двух_ раз. Но что-то мне подсказывает, что вы имели в виду более _одного_ раза.
Спасибо сказали:
Аватара пользователя
newsrc
Сообщения: 314
ОС: Slackware

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение newsrc »

Нет, именно повторы более двух раз, т.е. если строка встречается дважды, то её не удалять.

Нашёл вот такую дикую строку:

Код: Выделить всё

sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

Но она оставляет один экземпляр строки. И она выше моего понимания.
Система -- это совокупность элементов и связей, дающая новые свойства, не присущие ни одному из элементов и ни одной из связей.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение /dev/random »

newsrc писал(а):
08.06.2014 13:15
Нет, именно повторы более двух раз, т.е. если строка встречается дважды, то её не удалять.

Нашёл вот такую дикую строку:

Код: Выделить всё

sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

Но она оставляет один экземпляр строки. И она выше моего понимания.

А вам не надо оставлять этот экземпляр? Всё страннее и страннее. Ну ладно.

Предполагаю, что все дубли идут подряд. Если нет, приведите текст к такому виду с помощью sort.

* с помощью uniq -c замените каждую серию дублей на длину серии и строку
* затем sed'ом замените: s/^ *2 \(.*\)/\1\n\1/ (т.е. раскройте "2 строка" в "строка \n строка")
* им же удалите из строк s/^ *1 //
* им же (или grep'ом) удалите все остальные строки

Можно в другом порядке, если удобно.
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4823
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение SLEDopit »

/dev/random
У uniq есть опция -u, которая просто выведет все уникальные строки. Не нужно плясок с sed'ами/grep'ами.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение /dev/random »

SLEDopit писал(а):
08.06.2014 14:10
/dev/random
У uniq есть опция -u, которая просто выведет все уникальные строки. Не нужно плясок с sed'ами/grep'ами.

Я знаю, но в том-то и дело, что ТС это не подходит. Строки, встречающиеся 2 раза, ему удалять не нужно.
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4823
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение SLEDopit »

Ой, да, что-то я невнимательно прочитал, виноват.
Ну тогда, могу предложить решения для случая, если важно не нарушать порядок и размер файла относительно небольшой:

Код: Выделить всё

awk '{aprint[NR]=$0;if(!seen[$0]&&!twice[$0]){++seen[$0];}else{if(!twice[$0]){++twice[$0];}else{delete seen[$0]}}};END{for(i in aprint)if(seen[aprint[i]])print aprint[i];}'

суть работы примерно такая:
создаётся массив, куда вносится весь файл (aprint), второй массив, куда вносятся строки, которые встречались хоть раз ( seen ) и третий массив, где строки, которые встречались дважды ( twice ). Если строка встречается 3 и более раз, она удаляется из seen. В конце печатаются все пересечения из seen и aprint, которые и будут искомыми строками.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
newsrc
Сообщения: 314
ОС: Slackware

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение newsrc »

Спасибо всем.
В процессе чтения ваших постов придумал такой вариант:

Код: Выделить всё

#/bin/sh
foo() {
    while read line; do
        MATCH=`cat "${1}" | grep "${line}" | wc -l`
        if [[ ${MATCH} -le 2 ]]; then
            echo "${line}"
        fi
    done < "${1}"
}

TMP=`foo "${1}"`

echo "${TMP}" > ${1}


Да, постоянно делает cat файла, но размер в несколько строк позволяет так сделать.
Скрипту в качестве параметра передаётся имя файла.
Система -- это совокупность элементов и связей, дающая новые свойства, не присущие ни одному из элементов и ни одной из связей.
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4823
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение SLEDopit »

newsrc писал(а):
09.06.2014 09:13
cat "${1}" | grep "${line}" | wc -l
можно заменить на grep -c "$line" "$1". а ещё лучше на grep -c "^$line$" "$1", иначе могут быть лишние совпадения.
ну и да, построчная обработка файла на чистом bash'e всегда существенно дольше всяких sed'ов/awk'ов.

у вас, правда, файл, судя по всему маленький. но если размер вырастет, будет тяжело.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение NickLion »

Я бы написал вот так: perl -pe '$_="" unless $x{$_}++ < 2'
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4823
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение SLEDopit »

NickLion писал(а):
09.06.2014 13:27
Я бы написал вот так: perl -pe '$_="" unless $x{$_}++ < 2'
У вас оно выводит все повторяющиеся строки не более 2х раз (даже если оно повторяется 3 или 4 раза, 2 раза оно всё равно выведется). А ТС не нужно выводить строки, которые встречаются более 2х раз, вообще.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
newsrc
Сообщения: 314
ОС: Slackware

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение newsrc »

SLEDopit писал(а):
09.06.2014 12:17
а ещё лучше на grep -c "^$line$" "$1"


Да, вот это ещё лучше, спасибо. И за опцию -c спасибо.
Система -- это совокупность элементов и связей, дающая новые свойства, не присущие ни одному из элементов и ни одной из связей.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение /dev/random »

SLEDopit писал(а):
09.06.2014 12:17
можно заменить на grep -c "$line" "$1". а ещё лучше на grep -c "^$line$" "$1", иначе могут быть лишние совпадения.


Тогда уж лучше grep -cFx "$line", поскольку в $line может содержаться что-то, что grep примет за регулярку.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [РЕШЕНО] Удалить повторяющиеся строки

Сообщение NickLion »

SLEDopit писал(а):
09.06.2014 13:33
У вас оно выводит все повторяющиеся строки не более 2х раз (даже если оно повторяется 3 или 4 раза, 2 раза оно всё равно выведется). А ТС не нужно выводить строки, которые встречаются более 2х раз, вообще.

Да, значит, не понял задания. Можно так, конечно:
perl -e 'for(@a=<>){$x{$_}++} for (@a){if($x{$_}<=2){print $_}}'
или
perl -e 'for(@a=<>){$x{$_}++} print grep {$x{$_}<=2} @a'
Но уже более громоздко.
Спасибо сказали:
Ответить