GNU grep и русские буквы (помогите новичку разобраться)

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

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

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

GNU grep и русские буквы

Сообщение Nazyvaemykh »

Пытаюсь разобраться с программой GNU grep, пока не очень понимаю, как она работает. Поведение резко меняется от версии к версии, от того, как собрана и в каком окружении запускается. Отыскать смысл и истину пока получается не очень ):
Пожалуйста, помогите.

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

GNU grep 2.7
./configure --prefix=/usr --bindir=/bin --with-included-regex

$ echo ПРИВЕТ | LANG=C grep '[а-я]'
grep: Invalid collation character
$ echo ПРИВЕТ | LANG=ru_RU.UTF-8 grep '[а-я]'
ПРИВЕТ

./configure --prefix=/usr --bindir=/bin --without-included-regex
$ echo ПРИВЕТ | LANG=C grep '[а-я]'
$ echo ПРИВЕТ | LANG=ru_RU.UTF-8 grep '[а-я]'
$
GNU grep 2.5.3, Debian GNU/Linux Lenny

$ echo ПРИВЕТ | LANG=C grep  '[а-я]'
ПРИВЕТ
$ echo ПРИВЕТ | LANG=en_US.UTF-8 grep  '[а-я]'
$

GNU grep 2.6.3, Debian GNU/Linux Squeeze
$ echo ПРИВЕТ | LANG=en_US.UTF-8 grep '[а-я]'
ПРИВЕТ
$ echo ПРИВЕТ | LANG=ru_RU.UTF-8 grep '[а-я]'
ПРИВЕТ
$ echo ПРИВЕТ | LANG=C grep '[а-я]'
$

Посмотрел здесь, вроде бы, подходящих багрепортов нет, значит, это не ошибка, так и должно быть?
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Lan4
Сообщения: 339
Статус: hikki
ОС: Arch

Re: GNU grep и русские буквы

Сообщение Lan4 »

мне вообще не нравится, что под маску с маленькими буквами попадают большие(
у меня (юникод) работает, как "должно":

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

[ujen@tux ~]$ echo ПРИВЕТ | grep '[а-я]'
[ujen@tux ~]$ echo ПРИВЕТ | grep '[А-Я]'
ПРИВЕТ
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

Lan4, в принципе, это возможно. Действительно, зависит от локали. Не понятно только, какая это зависимость.
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
liaonau
Сообщения: 390
ОС: gentoo

Re: GNU grep и русские буквы

Сообщение liaonau »

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

~ $ echo ПРИВЕТ | LANG=ru_RU.UTF8 grep '[а-в]'
~ $ echo ПРИВЕТ | LANG=ru_RU.UTF8 grep '[а-г]'
ПРИВЕТ
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

liaonau, наверное, это логично:
[а-в] преврашается в [аАбБв],
[а-г] в [аАбБвВг]
а какая версия grep, как собиралась?
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Lan4
Сообщения: 339
Статус: hikki
ОС: Arch

Re: GNU grep и русские буквы

Сообщение Lan4 »

Nazyvaemykh писал(а):
09.03.2011 21:13
liaonau, наверное, это логично:
[а-в] преврашается в [аАбБв],
[а-г] в [аАбБвВг]
а какая версия grep, как собиралась?

а разве символы идут не так: сначала большие все, а потом маленькие?..
Спасибо сказали:
liaonau
Сообщения: 390
ОС: gentoo

Re: GNU grep и русские буквы

Сообщение liaonau »

Nazyvaemykh писал(а):
09.03.2011 21:13
liaonau, наверное, это логично:
[а-в] преврашается в [аАбБв],
[а-г] в [аАбБвВг]

Странно. Никогда раньше не задумывался и не замечал того, что вы привели в примере. Всегда думал, что [а-я] значить от а до я численно — от 0430 до 044f.
Nazyvaemykh писал(а):
09.03.2011 21:13
а какая версия grep, как собиралась?

GNU grep 2.5.4
./configure --prefix=/usr --build=i686-pc-linux-gnu --host=i686-pc-linux-gnu --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share --sysconfdir=/etc --localstatedir=/var/lib --bindir=/bin --enable-nls --enable-perl-regexp --without-included-regex
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

Lan4, это сильно зависит от локали, описывается в разделе LC_COLLATE.

По-видимому, это не GNU grep по-разному себя ведет, а просто в разных системах по-разному определены локали и правила сортировки символов.

Попробовал с английским языком, получил вот что:

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

Debian Squeeze, GNU grep 2.6.3:
# echo HELLO | LANG=en_US.UTF-8 grep '[a-z]'
HELLO
# echo HELLO | LANG=C grep '[a-z]'
#
А вот Arch Linux, GNU grep 2.7 --with-included-regex:
$ echo HELLO | LANG=en_US.UTF-8 ./grep '[a-z]'
$ echo HELLO | LANG=C ./grep '[a-z]'
$ echo HELLO | LANG=ru_RU.UTF-8 ./grep '[a-z]'
HELLO

¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
taaroa
Сообщения: 1319

Re: GNU grep и русские буквы

Сообщение taaroa »

Shell

$ bash -c "echo HELLO |grep '[a-e]'" $ bash -c "echo HELLO |grep '[A-E]'" HELLO $ grep "-V" grep (GNU grep) 2.4.2 Rev 1.4 (9-Jul-2001) Copyright 1988, 1992-1999, 2000 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


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

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

liaonau писал(а):
09.03.2011 21:26
Nazyvaemykh писал(а):
09.03.2011 21:13
liaonau, наверное, это логично:
[а-в] преврашается в [аАбБв],
[а-г] в [аАбБвВг]

Странно. Никогда раньше не задумывался и не замечал того, что вы привели в примере. Всегда думал, что [а-я] значить от а до я численно — от 0430 до 044f.

Не-не-не, это вообще не зависит от символов и кодировок. Вам может оказаться интересным пример про разницу испанской и «сшашевской» версий испанского языка (Arch Linux, GNU sed):

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

$ echo ll | LANG=C sed -n  "/^[a-z]$/p"
$ echo ll | LANG=es_ES.UTF-8 sed -n  "/^[a-z]$/p"
$ echo ll | LANG=es_US.UTF-8 sed -n  "/^[a-z]$/p"
ll

Раньше в испанском языке была двойная буква ll, в Испании ее отменили, в США — нет. Поэтому [a-z] в американской локали превращается в a,b,c,…,k,l,ll,m,n…z. От кодировки это не зависит.
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
liaonau
Сообщения: 390
ОС: gentoo

Re: GNU grep и русские буквы

Сообщение liaonau »

Nazyvaemykh писал(а):
09.03.2011 21:32
Не-не-не, это вообще не зависит от символов и кодировок. Вам может оказаться интересным пример про разницу испанской и «сшашевской» версий испанского языка (Arch Linux, GNU sed).

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

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

А вот sed в Debian Squeeze является в этом полной противоположностью grep:

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

# echo ПРИВЕТ | LANG=C sed -n  '/[а-г]/p'
ПРИВЕТ
# echo ПРИВЕТ| LANG=en_US.UTF-8 sed -n '/[а-г]/p'
#


Если допустить, что это поведение определяется локалью, содержимым /usr/share/i18n/locales, то либо sed, либо grep ошибается?
Либо это определяется чем-то другим, а не локалью? И каждая программа вольна трактовать такие диапазоны по-своему?
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

Странно, и то, что даже sed и grep в Arch, оба собранные с обцией --without-included-regex, ведут себя по-разному при LANG=C…
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux
Контактная информация:

Re: GNU grep и русские буквы

Сообщение eddy »

Хм, а у меня работает:

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

echo ПРИВЕТ | grep '[а-в]'
echo ПРИВЕТ | grep '[А-В]'
ПРИВЕТ
echo ПРИВЕТ | grep -i '[А-В]'
ПРИВЕТ
echo ПРИВЕТ | grep -i '[а-в]'
ПРИВЕТ

И если LANG менять на С:

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

echo ПРИВЕТ | LANG=C grep  '[а-я]'
echo ПРИВЕТ | LANG=C grep -i '[а-в]'
ПРИВЕТ
echo ПРИВЕТ | LANG=C grep -i '[А-Я]'
ПРИВЕТ
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: GNU grep и русские буквы

Сообщение ZyX »

Меня вот что прикалывает:

/tmp/zsh - 1 (Created by format.vim)

(zyx:/tmp) % echo HELLO | grep --color=always '[a-z]' HELLO (zyx:/tmp) % echo HELLO | grep -o '[a-z]' || echo "Error code $?" (zyx:/tmp) %



eddy писал(а):
10.03.2011 00:57
Хм, а у меня работает:

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

echo ПРИВЕТ | grep '[а-в]'
echo ПРИВЕТ | grep '[А-В]'
ПРИВЕТ
echo ПРИВЕТ | grep -i '[А-В]'
ПРИВЕТ
echo ПРИВЕТ | grep -i '[а-в]'
ПРИВЕТ

И если LANG менять на С:

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

echo ПРИВЕТ | LANG=C grep  '[а-я]'
echo ПРИВЕТ | LANG=C grep -i '[а-в]'
ПРИВЕТ
echo ПРИВЕТ | LANG=C grep -i '[А-Я]'
ПРИВЕТ

Добавьте туда ключ «-o», получите сюрприз.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux
Контактная информация:

Re: GNU grep и русские буквы

Сообщение eddy »

ZyX писал(а):
10.03.2011 01:41
Добавьте туда ключ «-o», получите сюрприз.

Пожалуйста:

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

ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | grep -o [а-в]'
ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | grep -o [А-В]'
В
ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | grep -io [А-В]'
В
ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | grep -io [а-в]'
В
ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | LANG=C grep -o [А-В]'
В
ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | LANG=C grep -o [а-в]'
ishtar> 10.03, 08:52 ~
echo ПРИВЕТ | LANG=C grep -oi [а-в]'
В
ishtar> 10.03, 08:53 ~
echo ПРИВЕТ | LANG=C grep -oi [А-в]'
В
echo ПРИВЕТ | LANG=C grep -oi '[А-Я]'
П
Р
И
В
Е
Т
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

eddy, давайте, догадаюсь, относительно новая версия grep, собранная с --without-included-regex?
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux
Контактная информация:

Re: GNU grep и русские буквы

Сообщение eddy »

Nazyvaemykh писал(а):
10.03.2011 09:15
eddy, давайте, догадаюсь, относительно новая версия grep, собранная с --without-included-regex?

Нет, первый случай - из мандривы 2009.1 (версию не припомню - это из дома писал), второй - из мандривы 2010:

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

grep --version
GNU grep 2.5.4

Как узнать, с какими ключами он был собран, понятия не имею.

P.S. Кстати, сравнивая работу свежих версий некоторых утилит с их старым поведением, иногда начинаешь задумываться о справедливости поговорки "ломать - не строить" :)
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: GNU grep и русские буквы

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

Как я вижу из подписи, у eddy однобайтная кодировка, а потому его сообщения в этой теме к вопросу отношения не имеют.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: GNU grep и русские буквы

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

Далее. LC_ALL=C вносит следующую путаницу:
$ echo 'ПРИВЕТ' | hexdump -c
0000000 320 237 320 240 320 230 320 222 320 225 320 242 \n
000000d
$ echo '[а]' | hexdump -c
0000000 [ 320 260 ] \n
0000005
"LC_ALL=C grep" будет искать _байты_, а не _символы_. Поэтому "echo 'ПРИВЕТ' | LC_ALL=C grep [а]" найдёт строку, т.к. в ней содержится один из искомых байтов - 320.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: GNU grep и русские буквы

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

В случае "LC_ALL=C grep '[а-я]'" делается попытка задать последовательность 260 - 321 (восьмеричная система) + ещё 2 отдельных байта. По стандарту, через дефис задаётся последовательность символов, а не просто байт. В то же время, LC_ALL=C задаёт работу с байтами, без учёта локали, а значит и без символов. Никакого "стандартного" способа разрешения этого противоречия нет, и очевидно, что разные реализации будут понимать это по-разному. Данной ситуации следует избегать.
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

Спасибо за ясный ответ!
Но все еще не понятна разница между grep и sed в Squeeze (sed 4.2.1, grep 2.6.3):

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

# echo HELLO | LC_COLLATE=en_US.UTF-8 sed -n '/[a-z]/p'
# echo HELLO | LC_COLLATE=en_US.UTF-8 grep '[a-z]'
HELLO


¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: GNU grep и русские буквы

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

Nazyvaemykh писал(а):
10.03.2011 14:22
Спасибо за ясный ответ!
Но все еще не понятна разница между grep и sed в Squeeze (sed 4.2.1, grep 2.6.3):

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

# echo HELLO | LC_COLLATE=en_US.UTF-8 sed -n '/[a-z]/p'
# echo HELLO | LC_COLLATE=en_US.UTF-8 grep '[a-z]'
HELLO

Ну что тут сказать... grep здесь соответствует стандарту, зато sed ведёт себя так, как хотелось бы большинству составителей скриптов. Формально это баг sed, но, полагаю, что они (да и я, наверное, тоже) назовут это фичой. Надо посмотреть, есть ли это у них в документации.
Спасибо сказали:
Аватара пользователя
t.t
Бывший модератор
Сообщения: 7390
Статус: думающий о вечном
ОС: Debian, LMDE

Re: GNU grep и русские буквы

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

А вот это вообще странно:

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

$ echo b | LC_COLLATE=C grep [A-C]
b
$ echo b | LC_ALL=C grep [A-C]
$


И в принципе, кто-то может понять, почему при любых языковых (т.е. не POSIX) локалях [a-c] раскрывается в [aAbBc], а [A-C] — в [AbBcC]? Где здесь логика?
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux
Контактная информация:

Re: GNU grep и русские буквы

Сообщение eddy »

[на правах офтопа]
И несмотря на все эти "косяки", мне все равно советуют сменить "динозавра" КОИ8-Р на юникод :)
[/на правах офтопа]
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
Nazyvaemykh
Сообщения: 438
Статус: Подопытный участник

Re: GNU grep и русские буквы

Сообщение Nazyvaemykh »

t.t,
вот такого, как в ваших примерах, у меня воспроизвести никак не получается… ни в Arch, ни в Squeeze, ни в Lenny.

[a-c] раскрывается в последовательность символов согласно локали, в лексикографическом порядке. Если считать, что большие буквы идут сразу за малыми (мы так обычно слова и сортируем), то все правильно. Не понятно, почему это правило иногда действует (grep собранный с --with-included-regex), а иногда — нет (sed и grep собранный --without-included-regex).
Из последовательности aAbBcCdDeE [a-c] «вырезает» именно aAbBc, [A-C] AbBcC.

По-идее, это должно быть понятно из файла /usr/share/i18n/locales/iso14651_t1_common. Но мне пока понятно мало.

eddy, разве эти «косяки» прямо связаны с кодировкой? (не считая случая с LC_COLLATE=C, который объяснил /dev/random)
¡ Страсть к разрушению есть творческая страсть!
Спасибо сказали:
Аватара пользователя
t.t
Бывший модератор
Сообщения: 7390
Статус: думающий о вечном
ОС: Debian, LMDE

Re: GNU grep и русские буквы

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

eddy писал(а):
10.03.2011 16:12
[на правах офтопа]
И несмотря на все эти "косяки", мне все равно советуют сменить "динозавра" КОИ8-Р на юникод :)
[/на правах офтопа]

Кодировки здесь ни при чём. См. мой предыдущий пост: там вообще латиница.
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux
Контактная информация:

Re: GNU grep и русские буквы

Сообщение eddy »

t.t писал(а):
10.03.2011 16:43
См. мой предыдущий пост: там вообще латиница.

У меня он почему-то тоже не так работает:

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

ishtar> 10.03, 16:55 ~
echo b | LC_COLLATE=C grep [A-C]
ishtar> 10.03, 16:55 ~
echo b | LC_ALL=C grep [A-C]
ishtar> 10.03, 16:55 ~
echo B | LC_COLLATE=C grep [A-C]
B
ishtar> 10.03, 16:55 ~
echo B  | LC_ALL=C grep [A-C]
B
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4823
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: GNU grep и русские буквы

Сообщение SLEDopit »

/dev/random писал(а):
10.03.2011 14:37
grep здесь соответствует стандарту, зато sed ведёт себя так, как хотелось бы большинству составителей скриптов.
egrep, кстати, тоже ведет себя как sed:

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

 $ echo $LANG
en_US.UTF-8
 $ echo AaBbCc | egrep -o "[a-z]"
a
b
c
 $ echo AaBbCc | egrep -o "[A-Z]"
A
B
C
 $ echo abc | egrep -o "[A-Z]"
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.
Спасибо сказали:
Аватара пользователя
t.t
Бывший модератор
Сообщения: 7390
Статус: думающий о вечном
ОС: Debian, LMDE

Re: GNU grep и русские буквы

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

Nazyvaemykh писал(а):
10.03.2011 16:43
t.t,
вот такого, как в ваших примерах, у меня воспроизвести никак не получается… ни в Arch, ни в Squeeze, ни в Lenny.
У меня почти чистый stable.

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

$ grep -V
GNU grep 2.6.3

Copyright (C) 2009 Free Software Foundation, Inc.
Лицензия GPLv3+: GNU GPL версии 3 или новее <http://gnu.org/licenses/gpl.html>
Это свободное ПО: вы можете продавать и распространять его.
Нет НИКАКИХ ГАРАНТИЙ до степени, разрешённой законом.

$ locale
LANG=ru_UA.UTF-8
LC_CTYPE="ru_UA.UTF-8"
LC_NUMERIC="ru_UA.UTF-8"
LC_TIME="ru_UA.UTF-8"
LC_COLLATE="ru_UA.UTF-8"
LC_MONETARY="ru_UA.UTF-8"
LC_MESSAGES="ru_UA.UTF-8"
LC_PAPER="ru_UA.UTF-8"
LC_NAME="ru_UA.UTF-8"
LC_ADDRESS="ru_UA.UTF-8"
LC_TELEPHONE="ru_UA.UTF-8"
LC_MEASUREMENT="ru_UA.UTF-8"
LC_IDENTIFICATION="ru_UA.UTF-8"
LC_ALL=ru_UA.UTF-8

Добавление LANG=C тоже ничего не меняет. В какой из переменных LC_* зарыта собака, пока не понял.

Nazyvaemykh писал(а):
10.03.2011 16:43
[a-c] раскрывается в последовательность символов согласно локали, в лексикографическом порядке. Если считать, что большие буквы идут сразу за малыми (мы так обычно слова и сортируем)

Начнём с того, что в русском языке заглавные буквы при сортировке должны идти _перед_ строчными. Но непонятно мне другое: зачем раскрытие шаблонов производится в порядке сортировки. Ведь [aAbBcC…zZ] в POSIX можно записать как [A-Za-z] — при сверке шаблонов (в отличие от сортировки) это то же самое. А вот [abc…z] и [ABC…Z] в языковых локалях никак не запишешь.
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
Спасибо сказали:
Ответить