Блог Олега Сердюкова

Решение проблемы с русскими буквами при соединении через ssh

Достаточно давно @andy_shev посоветовал мне уделить внимание переменным окружения, выставляемые при установлении ssh-соединения. Тогда мне это не показалось чем-то важным практически, но сегодня пришло время.

Проблема, с которой я столкнулся, оказалась странной. В Snow Leopard наконец-то Terminal.app стал нормально обрабатывать русские буквы в кодировке UTF-8, раньше же были проблемы с их отображением (они или “бились”, или же преобразовывались в набор восьмеричных чисел). Я вздохнул облегчённо - наконец-то я смог при работе в консоли комфортно писать по-русски.

Если же у вас до сих пор есть эта проблема, то прочитайте статью Папаши ”Отбивные из терминала”, и у вас всё получится. Правильная конфигурация такая:

Но когда я начал переносить свой сайт на второй ноутбук, я работал в основном с ним по ssh, и вот тут-то столкнулся с такой ситуацией - в локальном Terminal.app русские буквы работают идеально, а вот при заходе на такой же Mac OS X 10.6 по ssh вместо текста получаю набор восьмеричных чисел:

И это при том, что при работе через Screen Sharing с удалённым ноутбуком эта проблема не проявляется:

“Хммм…”, - сказал я и многозначительно почесал затылок. В чём же разница? А разница при всех прочих равных условиях (профайлы-то одинаковы) заключается в переменных окружения, которые выставляются при установке соединения по ssh. Идея намечена, проверяю.

Захожу через Screen Sharing, запускаю Terminal.app и сбрасываю переменные окружения в файл:

$ set > set.local

Захожу туда же через ssh:

$ set > set.ssh

Сравниваю и нахожу нужную мне переменную:

$ diff -u set.ssh set.local
+LC_CTYPE=UTF-8

Опять захожу через ssh и пробую выставить её вручную

$ export LC_CTYPE=UTF-8

Ничего не меняется, значит настройка кодировки производится при запуске bash. Так, за это отвечает библиотека readline, а параметры bash, связанные с ней, можно посмотреть через “bind -v”. Повторяю процесс сравнения, в чём же разница:

(screen sharing) $ bind -v > bind.local
(ssh) $ bind -v > bind.ssh
$ diff -u bind.ssh bind.local
-set convert-meta on
+set convert-meta off

-set input-meta off
+set input-meta on

-set meta-flag off
+set meta-flag on

-set output-meta off
+set output-meta on

Нашёл. Чтобы эти параметры задействовать, их нужно поместить в файл ~/.inputrc и перезпустить bash:

$ cat ~/.inputrc
set convert-meta off
set input-meta on
set meta-flag on
set output-meta on

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

Проблема-то решена, но сделаем ещё один шаг. Конфигурации-то одинаковы, почему же переменная LC_TYPE не передаётся через ssh? Смотрю man ssh и в секции ENVIRONMENT нахожу, что при соединении ssh выставляются только такие переменные окружения: DISPLAY, HOME, LOGNAME, MAIL, PATH, SSH_ASKPASS, SSH_AUTH_SOCK, SSH_CONNECTION, SSH_ORIGINAL_COMMAND, SSH_TTY, TZ, USER. И самое ценное:

Additionally, ssh reads ~/.ssh/environment, and adds lines of the format “VARNAME=value” to the envi-ronment environment if the file exists and users are allowed to change their environment.

Удаляю на удалённом хосте (тавтология) файл ~/.inputrc, делаю файл ~/.ssh/environment и правлю /etc/sshd_config:

$ rm ~/.inputrc
$ vim ~/.ssh/environment
LC_CTYPE=UTF-8
$ vim /etc/sshd_config
...
PermitUserEnvironment yes
...

Перезапускать sshd не нужно. Захожу по ssh, и вижу, что переменная окружения LC_TYPE выставилась, и русские буквы работают.

Вот такие два метода решения проблемы я нашёл. Каким из них воспользоваться - выбирать вам. Мне нравится второй, но нужно знать и первый. Только что проделал трюк с PermitUserEnvironment на удалённом хосте с FreeBSD - и там тоже проблема с русскими буквами исчезла.

Update. Спасибо @Ivader за подсказку - более правильно вместо PermitUserEnvironment использовать AcceptEnv:

$ vim /etc/sshd_config
AcceptEnv LANG LC_*

К сожалению, сейчас не могу попробовать этот рецепт на Mac OS X, но он должен работать.

Comments