Как стать автором
Обновить

Обходим ошибки утилит из пакета GNU Core Utilities

Время на прочтение3 мин
Количество просмотров6.7K
TerminalПакет coreutils предустановлен во многих дистрибутивах Linux. Он содержит в себе стандартные и такие привычные утилиты, как cat, chmod, date, echo, ls и многие другие. Но даже в таком каноническом пакете встречаются ошибки, которые могут помешать работе пользователя. С одной из них я столкнулся на собственном опыте и хочу рассказать о том, как смог её обойти.

Задача была следующая – преобразовать текстовый файл с длинными строками так, чтобы ни одна строка не была длиннее 80 символов. Длинные строки должны разбиваться на несколько строк по 80 или менее символов. Файл закодирован в UTF-8. Немного погуглив можно узнать, что в Unix-подобных ОС с этой задачей справляется утилита fold. Отлично, значит, будем её использовать. Для начала выполним в терминале пару тестовых команд, чтобы научиться управляться с ней. Я приведу ниже вывод команд, выполненных в системе Debian 7.5 с пакетом coreutils 8.13. Такой же вывод будет и в системе Arch Linux с coreutils 8.22.

При выполнении всех тестовых команд настройки локали следующие:

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

Если у вас не так, то выполните:

$ export LC_ALL="ru_RU.UTF-8"

Пусть тестовая команда разобьёт строку «abcdefghij» на строки по 4 символа:

$ echo "abcdefghij" | fold -w 4
abcd
efgh
ij

Здорово! Теперь строку «абвгдеёжзи»:

$ echo "абвгдеёжзи" | fold -w 4
аб
вг
де
ёж
зи

И тут-то нас ждет сюрприз. Видим, что строка «абвгдеёжзи» разбилась на строки по два символа. Дело тут в том, что кириллический символ в кодировке UTF-8 занимает два байта, а символ латиницы один. Утилита fold, считая все символы однобайтовыми, просто разбила данную строку (массив байт) на куски по 4 байта. Как видно, такой алгоритм разбиения верен в кодировке UTF-8 только для латинских символов. В то же время утилита wc верно подсчитает количество символов в строке «абвгдеёжзи»:

$ echo -n "абвгдеёжзи" | wc -m
10

Это говорит о том, что поддержка юникода в пакете coreutils реализована частично, и результат работы с юникодом различных утилит может быть непредсказуемым.

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

Описанное выше не относится к BSD системам, у них собственная реализация стандартных утилит. Тест в системе FreeBSD 10 показал, что там с юникодом всё в порядке.

Теперь поговорим о том, как обойти эту ошибку. Мне известны две замены coreutils: BusyBox и Heirloom. Первый вариант мне показался более актуальным и простым, поэтому покажу как с его помощью соорудить костыль, который позволит нормально пользоваться утилитой fold в вашей системе. Аналогичным образом можно соорудить костыль и для любой другой стандартной утилиты.

Для начала установим пакет busybox. В системе Debian команда:

# apt-get install busybox

В системе Arch Linux, соответственно, такая команда:

# pacman -S busybox

Согласно документации, использовать BusyBox можно так:

$ busybox ls -l
$ busybox ps
$ busybox seq 1 5

Т.е. просто передавать имя утилиты как параметр исполняемому файлу busybox. Можно также переименовать исполняемый файл в одну из поддерживаемых им команд, и он будет автоматически действовать так, как будто это и есть эта команда. Переименовывать мы его не будем, но вот символьную ссылку с именем fold на него создадим:

# cd $(dirname $(which fold))
# mv fold fold.orig
# ln -s $(which busybox) fold

После этого fold можно использовать самым привычным образом: вызывать из терминала или скрипта. Такая заплатка в системе является для меня приемлемой. Буду рад, если кому-то она тоже сможет помочь. А пока остаётся надеяться, что когда-нибудь coreutils будет полностью поддерживать юникод.
Теги:
Хабы:
-6
Комментарии40

Публикации

Истории

Работа

Ближайшие события