Выбор строк по длине

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

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

sedikpoll
Сообщения: 100

Выбор строк по длине

Сообщение sedikpoll »

Есть текущий набор строк:

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

dfdsfdsfsdfsdf
444444444444444
555555g
еееееееееееее
sssg
2222222222222222222
4
34444444444444
о
555


Нужно из него выбрать строчки определенной длины. Например, строчки у которые больше 5 символов.

Нашел одно решение: awk 'length() > 5' file //строки больше 5 символов.

А так же, самые длинные(2222222222222222222) и короткие(4, о) строчки.
Подскажите куда смотреть.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Выбор строк по длине

Сообщение drBatty »

sedikpoll писал(а):
01.08.2010 03:47
Нужно из него выбрать строчки определенной длины. Например, строчки у которые больше 5 символов.

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

$ sed -rn '/.{6,}/p'
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

Самую длинную строчку можно найти таким конвеером:

cat file |while read -r s; do echo $(echo $s| wc -c) $s; done |sort -grk1 | head -n1

Для самой короткой строки можно использовать простой sed-скрипт:

sed ':B;N;h;s/[^\n]/a/g;/^\(a*\)\n\1/bA;g;s/^[^\n]*\n//;bB;:A;g;s/\n.*//;bB' file
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
KiWi
Бывший модератор
Сообщения: 2521
Статус: статус, статус, статус

Re: Выбор строк по длине

Сообщение KiWi »

Nazyvaemykh писал(а):
01.08.2010 15:10
Самую длинную строчку можно найти таким конвеером:

cat file |while read -r s; do echo $(echo $s| wc -c) $s; done |sort -grk1 | head -n1

Для самой короткой строки можно использовать простой sed-скрипт:

sed ':B;N;h;s/[^\n]/a/g;/^\(a*\)\n\1/bA;g;s/^[^\n]*\n//;bB;:A;g;s/\n.*//;bB' file

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

cat file | awk '{ print length($0), $0; }' | sort -grk 1 | head -n1 | cut -d' ' -f'2-'

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

cat file | awk '{ print length($0), $0; }' | sort -gk 1 | head -n1 | cut -d' ' -f'2-'


Не?

А ещё:

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

cat file | awk 'BEGIN { LEN=-1; STR=""; }; END { print STR; }; { if (length($0) > LEN || LEN == -1) { LEN=length($0); STR=$0; } }'


Пример для самой короткой строки можно написать самостоятельно)
Спасибо сказали:
Аватара пользователя
t.t
Бывший модератор
Сообщения: 7390
Статус: думающий о вечном
ОС: Debian, LMDE

Re: Выбор строк по длине

Сообщение t.t »

Nazyvaemykh писал(а):
01.08.2010 15:10
Самую длинную строчку можно найти таким конвеером:

cat file |while read -r s; do echo $(echo $s| wc -c) $s; done |sort -grk1 | head -n1
Да и самую короткую можно аналогично (на мой взгляд, куда проще, чем Ваш «простой» sed-скрипт). Но это не полное решение: самых длинных (коротких) строк может быть несколько.
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: Выбор строк по длине

Сообщение IMB »

Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5404
ОС: Gentoo

Re: Выбор строк по длине

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


Слишком медлительно. Хотя в качестве дополнения к указанному выше "конвейеру" сойдёт.
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

t.t писал(а):
01.08.2010 15:54
Да и самую короткую можно аналогично (на мой взгляд, куда проще, чем Ваш «простой» sed-скрипт). Но это не полное решение: самых длинных (коротких) строк может быть несколько.

Ну, мой sed-скрипт если не проще, то, во всяком случае, короче варианта на awk, предложенного KiWi (:

Полное решение, с поиском всех самых коротких строк, может выглядеть так:

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

#!/bin/sed -f

:B
N;h;s/[^\n]/a/g;
/^\(a*\)\n\(.*\n\)\?\1$/ {g;;bB}
/^\(a*\)\n\(.*\n\)\?\1a\+$/ {g;s/\n[^\n]*$//;bB}
g;s/^.*\n\([^\n]*$\)/\1/;bB;
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
Luinnar
Сообщения: 246
ОС: Solaris, Debian, Ubuntu

Re: Выбор строк по длине

Сообщение Luinnar »

awk-скрипт всё же более нагляден по сравнению с sed'ом.) sed тоже хорош для своих задач, просто порой не такой наглядный, как awk. Последний лаконичен. ^_^
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: Выбор строк по длине

Сообщение IMB »

Luinnar писал(а):
04.08.2010 14:57
awk-скрипт всё же более нагляден по сравнению с sed'ом.) sed тоже хорош для своих задач, просто порой не такой наглядный, как awk.

Поддерживаю. Пример - писал скрипт для изменнеия сетевых настроек..

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

#sed
ADDR=`sed -nr 's/^address\s(.*)$/\1/p' $FILE`
#awk
ADDR=`awk '$1 ~ /address/ {print $2}' $FILE`

Но думаю с увелечением практики в sed это разница нивелируется.
Спасибо сказали:
Аватара пользователя
sash-kan
Администратор
Сообщения: 13939
Статус: oel ngati kameie
ОС: GNU

Re: Выбор строк по длине

Сообщение sash-kan »

IMB писал(а):
04.08.2010 15:55
#sed
ADDR=`sed -nr 's/^address\s(.*)$/\1/p' $FILE`
#awk
ADDR=`awk '$1 ~ /address/ {print $2}' $FILE`
проще надо быть:
sed -n 's/^address\s//p' $FILE

а что такое
awk '$1 ~ /address/ {print $2}' $FILE
действительно, фиг догадаешься.
Писать безграмотно - значит посягать на время людей, к которым мы адресуемся, а потому совершенно недопустимо в правильно организованном обществе. © Щерба Л. В., 1957
при сбоях форума см.блог
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: Выбор строк по длине

Сообщение IMB »

sash-kan писал(а):
04.08.2010 17:31
проще надо быть:
sed -n 's/^address\s//p' $FILE

У меня была задача сохранить значение в переменную. Стесняюсь спросить, приведённая команда выполнить аналогичное действие?
sash-kan писал(а):
04.08.2010 17:31
а что такое
awk '$1 ~ /address/ {print $2}' $FILE
действительно, фиг догадаешься.

Ну кому как, на моём уровне развития понятнее ;)
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Выбор строк по длине

Сообщение drBatty »

ИМХО самые длинные/короткие строки проще всего можно найти простой заменой всех символов на какой-нибудь неиспользуемый символ, что-то вроде
sed 's/./~/g'
после чего, получившиеся строки надо отсортировать, тогда самые короткие(длинные) строки будут в начале(конце) списка:

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

h
s/./~/g
s/$/                                                                                          /
H
g
s/\n/                                                                                         /

(должно работать если строки не слишком длинные, и не начинаются с / +/, ненужный мусор потом можно отфильтровать так:

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

s/^~* +//

http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
sash-kan
Администратор
Сообщения: 13939
Статус: oel ngati kameie
ОС: GNU

Re: Выбор строк по длине

Сообщение sash-kan »

IMB писал(а):
04.08.2010 18:05
Стесняюсь спросить, приведённая команда выполнить аналогичное действие?
на всех комбинациях входных данных, которые я смог вообразить, эти две команды действуют идентично:
sed -nr 's/^address\s(.*)$/\1/p' $FILE
sed -n 's/^address\s//p' $FILE

а вот приведённый вариант на awk действует совершенно иначе. например, он выдаст «123», если в файле содержится, например,

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

   badaddressiswrong 123
Писать безграмотно - значит посягать на время людей, к которым мы адресуемся, а потому совершенно недопустимо в правильно организованном обществе. © Щерба Л. В., 1957
при сбоях форума см.блог
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: Выбор строк по длине

Сообщение IMB »

sash-kan писал(а):
05.08.2010 01:44
а вот приведённый вариант на awk действует совершенно иначе. например, он выдаст «123», если в файле содержится, например,

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

   badaddressiswrong 123

Это понятно, условие стоит менее строгое, но данный пример приводился как "более" понятный синтаксис, чем у sed.
Спасибо сказали:
Аватара пользователя
t.t
Бывший модератор
Сообщения: 7390
Статус: думающий о вечном
ОС: Debian, LMDE

Re: Выбор строк по длине

Сообщение t.t »

IMB писал(а):
05.08.2010 08:56
sash-kan писал(а):
05.08.2010 01:44
а вот приведённый вариант на awk действует совершенно иначе. например, он выдаст «123», если в файле содержится, например,

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

   badaddressiswrong 123
Это понятно, условие стоит менее строгое, но данный пример приводился как "более" понятный синтаксис, чем у sed.
А Вы напишите для awk полный вариант и сравните не с Вашим бесполезно перегруженным для sed, а с вариантом от sash-kan (как Александр уже сказал, по результату они эквивалентны) — тогда и посмотрим, что проще.
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

drBatty,
спасибо, способ довольно интересный, только не понятно, зачем такие длинные последовательности пробелов?
и /~+/ оказывается в конце строчек, а не в начале?

Такой конвейер делает то же самое?

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

sed 'h; s/./a/g; G; s/\n/ /' | sort | sed 's/^a* //'
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

Попробовал посравнивать разные варианты на предмет быстродействия, отыскивая самые длинные строки в файле /var/log/pacman.log, там уже 25 тыс. строк.

Самым быстрым оказался awk (GNU Awk 3.1.8), принимаем его время за единицу.
Далее вариант drBatty (GNU sed версии 4.2.1), его время 14.
На третьем месте „чистый bash“ (GNU bash, version 4.1.7), его время 28. Не так уж и медлительно, можно сказать.
Мой простенький sed-скрипт отстает от awk в 371 раз.
А вот конвейер, типа приведенного в сообщении #3, работает действительно медленно. Его время 1439.
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
KiWi
Бывший модератор
Сообщения: 2521
Статус: статус, статус, статус

Re: Выбор строк по длине

Сообщение KiWi »

Nazyvaemykh писал(а):
06.08.2010 19:39
А вот конвейер, типа приведенного в сообщении #3, работает действительно медленно. Его время 1439.

А кто-то говорил, что он будет работать быстро?)
Передача данных между приложениями(запись в одном, чтение в другом).
И памяти жрать оно будет, скорее всего, размером с ВЕСЬ файл -- чтобы файл отсортировать его нужно весь прочитать)
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

Тут дело не в сортировке, во втором по быстродействию варианте (вариант drBatty) тоже сортировка используется… bash без сортировки, но с ${#string}, работает в два раза медленнее.
Причина, как я понимаю, в непрерывном дёрганье wc для каждой строки.
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Выбор строк по длине

Сообщение drBatty »

sash-kan писал(а):
05.08.2010 01:44
на всех комбинациях входных данных, которые я смог вообразить, эти две команды действуют идентично:
sed -nr 's/^address\s(.*)$/\1/p' $FILE
sed -n 's/^address\s//p' $FILE

результат будет разным если есть НЕсимволы. например дырки в UTF - 0xd1d1 например - это не совпадает с точкой.
Nazyvaemykh писал(а):
06.08.2010 19:39
Самым быстрым оказался awk (GNU Awk 3.1.8), принимаем его время за единицу.
Далее вариант drBatty (GNU sed версии 4.2.1), его время 14.
На третьем месте „чистый bash“ (GNU bash, version 4.1.7), его время 28. Не так уж и медлительно, можно сказать.
Мой простенький sed-скрипт отстает от awk в 371 раз.

сортировка и подсчёт - это не задача для sed.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: Выбор строк по длине

Сообщение ZyX »

Nazyvaemykh писал(а):
08.08.2010 11:15
Тут дело не в сортировке, во втором по быстродействию варианте (вариант drBatty) тоже сортировка используется… bash без сортировки, но с ${#string}, работает в два раза медленнее.
Причина, как я понимаю, в непрерывном дёрганье wc для каждой строки.

Bash вообще медленный:

zyx@zyx-desktop

(zyx:~) % time ( cat /var/log/emerge.log |while read -r s; do echo ${#s} $s; done |sort -grk1 | head -n1 ) > /dev/null (; cat /var/log/emerge.log | while read -r s; do; echo ${#s} $s; done | sort 0,00s user 0,00s system 0% cpu 4,297 total (zyx:~) % time bash -c 'cat /var/log/emerge.log |while read -r s; do echo ${#s} $s; done |sort -grk1 | head -n1' > /dev/null bash -c > /dev/null 11,40s user 2,17s system 102% cpu 13,252 total (zyx:~) % echo $ZSH_VERSION 4.3.10 (zyx:~) % bash -c 'echo $BASH_VERSION' 4.0.37(2)-release


Остальные варианты:

zyx@zyx-desktop

(zyx:~) % time ( cat /var/log/emerge.log | awk '{ print length($0), $0; }' | sort -grk 1 | head -n1 | cut -d' ' -f'2-' ) >/dev/null (; cat /var/log/emerge.log | awk '{ print length($0), $0; }' | sort -grk 1 | 0,00s user 0,00s system 0% cpu 1,190 total (zyx:~) % time ( cat /var/log/emerge.log | awk 'BEGIN { LEN=-1; STR=""; }; END { print STR; }; { if (length($0) > LEN || LEN == -1) { LEN=length($0); STR=$0; } }' ) > /dev/null (; cat /var/log/emerge.log | awk ; ) > /dev/null 0,46s user 0,01s system 99% cpu 0,471 total (zyx:~) % time ( cat /var/log/emerge.log | sed 'h; s/./a/g; G; s/\n/ /' | sort | sed 's/^a* //' ) > /dev/null (; cat /var/log/emerge.log | sed 'h; s/./a/g; G; s/\n/ /' | sort | sed ; ) > 0,17s user 0,02s system 2% cpu 8,375 total


Кстати, если переписать самый быстрый вариант с awk на perl, то получится намного быстрее:

zyx@zyx-desktop

(zyx:~) % time ( perl -p -e 'BEGIN { @L=(-1) } END { print $L[-1] } @L=((length), $_) if((length)>$L[0]); undef $_;' /var/log/emerge.log ) > /dev/null (; perl -p -e /var/log/emerge.log; ) > /dev/null 0,08s user 0,01s system 99% cpu 0,086 total


Всё это в таблице:








ВариантАбсолютное времяОтносительное время
Nazyvaemykh (bash) 13,2523,08
Nazyvaemykh (sed) 8,3751,95
Nazyvaemykh (zsh) 4,2971,00
KiWi (awk+coreutils) 1,1900,28
KiWi (awk) 0,4710,11
ZyX (perl) 0,0860,02

Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

ZyX, варианты с head -n 1 не годятся, как заметил выше
t.t, самых длинных строк может быть несколько.

Для bash я использовал такой скрипт (чисто bash, без команды sort):

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

#!/bin/bash
MAX=0
STR=""
while read -r s; do
    CUR=${#s}
    if test $CUR -gt $MAX; then
        MAX=$CUR
        STR="$s"
    elif test $CUR -eq $MAX; then
        STR=$(echo -e "$STR""\n$s")
    else
        continue
    fi
done

echo "$STR"


Соответственно, и в конец конвейера чуть-чуть дописал (это не сильно влияет на производительность при небольшом числе самых длинных строк):

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

#!/bin/sh
while read  s; do
    echo "$(echo $s| wc -c)" "$s";
done |sort -grk1 | while read -r s; do
    CUR=$(echo "$s"|wc -c)
    if test -z $TOP; then
        TOP=$CUR
    fi
    if test $TOP -eq $CUR; then
        echo "$s" | cut -d' ' -f2-
    else
        break
    fi
done
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Выбор строк по длине

Сообщение drBatty »

ZyX
спасибо за измерения, но ИМХО слишком малое время. как я понимаю, весь файл был в оперативной памяти?
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: Выбор строк по длине

Сообщение ZyX »

drBatty писал(а):
08.08.2010 16:05
ZyX
спасибо за измерения, но ИМХО слишком малое время. как я понимаю, весь файл был в оперативной памяти?

Да, нам же не нужно мерить производетельность IO.
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

Немного усложнил sed-скрипт, находящий самые длинные строки, и его производительность увеличилась почти на треть.

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

#!/bin/sed -f
:B
N;h;s/[^\n]/a/g;
tA;:A
s/^\(a*\)\n\(.*\n\)\?\1\(a*\)$/\3/;tC
g;s/\n[^\n]*$//;bB;
:C
/^$/ {g;bB;}
g;s/^.*\n//;bB
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: Выбор строк по длине

Сообщение Nazyvaemykh »

Еще немного усложнил скрипт, работает заметно быстрее, всего лишь в 86 раз медленнее awk на файле 25 тыс. строк:

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

#!/bin/sed -f
1 {h;s/./a/g;G;}
:B
$ {s/^a*\n//;q;}
h;s/\n.*//;N;H;
s/[^\n]/a/g;
tA;:A
s/^\(a*\)\n\1//;tC
g;s/\na*\n[^\n]*$//;bB
:C
/^$/ {g;s/a*\n\([^\n]*\)$/\1/;bB;}
g;s/.*\n//;h;s/./a/g;G;bB;
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали: