Oracle instant client 11 + php + oci8 + SHELL = Проблема с unicode.

ВНИМАНИЕ: Много буков!

Уважаемые коллеги!
Имею следующую задачу: нужно каждую ночь переливать данные из одной СУБД (ORACLE) в другую (MySQL). Обе СУБД - с поддержкой UNICODE, исходная таблица содержит кириллические символы. Процесс “синхронизации” построен на php+oci8.so и имеет довольно простой код. Весь код приводить здесь смысла не вижу, укажу лишь, что в цикле я присваиваю переменной $A значение определенного столбца таблицы ORACLE, а затем это же значение заливаю в схожую таблицу на MySQL. Все это дело крутиться на SLES:

root@ukuwbpeeuintra:orasync# uname -a
Linux ukuwbpeeuintra 2.6.16.60-0.54.5-bigsmp #1 SMP Fri Sep 4 01:28:03 UTC 2009 i686 i686 i386 GNU/Linux

Собственно, проблема заключается в том, что в зависимости от SHELL, из-под которого вручную запускается скрипт, данные из ORACLE я получаю либо в виде “/?/?/?/?/?” либо в нормальном виде с русскими буквами. Мои поиски правды привели меня к тому, что скрипт

/usr/local/bin/php -f get-ddd-info.php

правильно отрабатывает только под bash. Под остальными SHELL вместо русских буковок я на выходе (еще до сохранения данных в MySQL) получаю “/?/?/?/?/?”. Анализ расхождений между SHELL меня ни к чему не привели. Насильное присваивание переменных окружения для ORACLE

LD_LIBRARY_PATH=/usr/lib/oracle/instantclient_11_1
ORACLE_BASE=/usr/lib/oracle
ORACLE_HOME=/usr/lib/oracle/instantclient_11_1
ORA_NLS10=/usr/lib/oracle/instantclient_11_1/nls/data
ORA_NLS33=/usr/lib/oracle/instantclient_11_1/nls/data
TNS_ADMIN=/usr/lib/oracle/instantclient_11_1/network/admin

так же не дают ожидаемого результата.

Смирившись с таким положением дел, я решил автоматизировать процесс “синхронизации”, поместив соответствующую строку в общесистемный crontab. Разумеется, я тут же получил на выходе “/?/?/?/?/?”. Поняв, что системный crontab запускается в окружении /bin/sh, я решил перенести процесс “синхронизации” в пользовательский crontab, предварительно указав внутри, что запускать все процессы нужно в окружении /bin/bash. Результат - опять “/?/?/?/?/?”. Тогда я внутри пользовательского crontab прописал необходимые переменные, но и это не помогло:

# SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/root/bin
MAILTO=root
LD_LIBRARY_PATH=/usr/lib/oracle/instantclient_11_1
ORACLE_BASE=/usr/lib/oracle
ORACLE_HOME=/usr/lib/oracle/instantclient_11_1
ORA_NLS10=/usr/lib/oracle/instantclient_11_1/nls/data
ORA_NLS33=/usr/lib/oracle/instantclient_11_1/nls/data
TNS_ADMIN=/usr/lib/oracle/instantclient_11_1/network/admin
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
#
####################################################
# Get the latest information about DDD #
####################################################
# 35    11      *       *       *       cd /srv/www/orasync/public_html/; /usr/local/bin/php -f get-ddd-info.php
#

С завидной регулярностью на выходе я получал все те же знаки вопроса вместо ожидаемых кирилических символов. Тогда я соорудил небольшой *.sh файл, куда запихнул переменные окружения, запуск “синхронизатора”, а в пользовательском crontab заменил стрку запуска на

#*      12      *       *       *       cd /srv/www/orasync/; /bin/bash get-ddd.sh

К привеликому моему сожалению, это тоже не помогло.

Помогите, пожалуйста, советом или сслыкой на правильный ресурс.

P.S. Рядом стоит обычная opensuse, на которой этот же php скрипт прекрасно работает. Игра “найди десять отличий между двумя системами” результатов не принесла.

Кстати, по поводу поиска отличий. Есть подозрение, что отличия таки есть - locale для разных SHELL показывают разную информацию:
Для /bin/tcsh:

root@ukuwbpeeuintra:orasync# locale
LANG=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

Для /bin/bash:

orasync@ukuwbpeeuintra:~> locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

В этой связи есть вопрос: почему в пределах одной машины для разных SHELL мы имеем разные настройки консоли и, самое главное, как их сменить?

Спасибо заранее всем ответившим и откликнувшимся!

tcsh от рута запущен, а bash?

Системная локаль меняется через YaST или в /etc/sysconfig/language. Пользовательская – в конфигурационных файлах профиля.

Lazy_Kent, и тот и другой SHELL запускаются из-под обычных пользователей, не от root’а. locale’ы для обычных пользователей выглядят именно так, как я указал в своем предыдущем посте.
Системные настройки имеют вот такой вид:

root@ukuwbpeeuintra:izemlian# cat /etc/sysconfig/language | grep -v #
RC_LANG="en_US.UTF-8"
RC_LC_ALL=""
RC_LC_MESSAGES=""
RC_LC_CTYPE=""
RC_LC_COLLATE=""
RC_LC_TIME=""
RC_LC_NUMERIC=""
RC_LC_MONETARY=""
RC_LC_PAPER=""
ROOT_USES_LANG="ctype"
AUTO_DETECT_UTF8="no"
INSTALLED_LANGUAGES=""

Тут другое не понятно. Первое: почему для bash и для tcsh разные настройки консоли и где их ковырять? А второе - это почему скрипт, запущенный из-под пользовательского crontab с явным указанием интерпретатора, теряет настройки locale?

Спасибо.

root@ukuwbpeeuintra:orasync# locale
Я ясно вижу, что tcsh запущен от рута.

для bash и для tcsh разные настройки консоли и где их ковырять?
В ~/.bashrc, ~/.bash_profile и ~/.tcshrc, вероятно.

Хм, согласен. Вот вывод для обычного пользователя:

izemlian@ukuwbpeeuintra:~> locale
LANG=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

Все равно ситуация не меняется.

Вот вы говорите, что править нужно пользовательские bashrc, ~/.bash_profile и ~/.tcshrc. А разве не дадут тот же эффект настройки в /etc/bash.bashrc и /etc/csh.cshrc только на глобальном уровне?

Какие переменные нужно присваивать? Те же, что и в /etc/sysconfig/language?

Спасибо заранее.

Пользовательские настройки переменных перекрывают глобальные. Я бы не стал глобальные конфиги трогать.

А в самом скрипте “LANG=en_US.UTF-8” не помогает?

К сожалению, где бы я не устанавливал переменную

LANG=en_US.UTF-8

это не помогает. А устанавливал я пеерменнюу и внутри пользоватльского crontab и внутри SHELL скрипта, который запускает интерпретатор php:

cd /srv/www/orasync/public_html/; /usr/local/bin/php -f get-ddd-info.php

Есть какие-то другие мысли по этому поводу? :slight_smile:

Ребят посмотрите моё решение,может поможет openSUSE & utserver

Коллеги,

К сожалению, решение с экспортом переменных не помогает. Похоже, что библиотека ORACLE чувствительна к чему-то еще, кроме этих переменных.

Спасибо.