Скрипт bash для корректного обновления серверной части платформы 1С Предприятия (сохраняет и восстанавливает настройки служб, даже если их несколько, изменяет версию платформы во всех веб-публикациях и делает еще много полезных мелочей не включенных в штатный инсталлятор) Иными словами “обновление под ключ” или “нажал и забыл”
Телефонный опрос, проведенный утром 1 января, дал следующие результаты:2% - "да?" 3% - "алло?" 95% - затруднились ответить
www.anekdot.ru©
Новый Год у сисадмина отличается от обычных дней тем, что на столе рядом с пивом лежат мандаринки ...
сетевой фольклор©
Ведь именно новогодние каникулы, это то время, пока коллеги лежат носом в оливье, ИТ-шники всех стран обновляют сервера, сворачивают базы, переходят на новые релизы.
В ноябре уже пора об этом задумываться.

Внимание! Все действия нужно производить осознанно и понимая их последствия
Скрипт предоставляется бесплатно по принципу "AS IS" / "Как есть".
Автор гарантирует, что никаких действий, кроме обновления серверной части платформы 1С, скрипт не производит, но не несёт ответственности за ваши данные.
Постановка задачи
Установщики платформы 1С от вендора для linux, да и для windows тоже, как виде для пакетного менеджера, так в виде программы установки сделанной VMWare InstallBuilder не умеют и скорее всего не будут уметь, сохранять настройки для службы сервера 1С сделанные вручную, особенно если серверов 1С несколько на разных портах на одном логическом сервере.
Тем более обновлять версии платформы во всех веб-публикациях.
Для не совсем внимательного пользователя это может быть проблемой.
Тем более если этот пользователь работает на linux.
Импортозамещение шагает по стране уже два года, но для linux пока не нашел ничего достойного.
Поэтому привел свой скрипт в удобоваримый вид (не обошлось без помощи gemini) и просто положу его здесь.
Решение
Скрипт на bash, который проверяет.
что запущен с правами администратора
наличие архива установки или распакованного файла установки
если выбрано обновление, то соответствие установленной версии платформы и заданной ключом командной строки
сохраняет файлы старых служб сервера 1С и ras с возможными нетиповыми настройками
создает новые файлы служб сервера 1С и ras с перенесенными настройками
останавливает службу сервера 1С и ras
устанавливает новую версию платформы 1С
переносит в ее каталог новые файлы служб сервера 1С и ras
запускает службы сервера 1С и ras
при успешном запуске удаляет старую версию платформы 1С
во всех найденных веб-публикациях изменяет версию платформы 1С
перезапускает веб-сервер
Выполняется как любой скрипт.
Может обновлять 8.3.х.х на 8.5.х.х и обратно, что не менее актуально в настоящий момент см. багтрекер 1С.
Любители острых ощущений могут его даже в cron запланировать на ночь и утром удивиться результату.

Использование скрипта: $PROGNAME параметр1 параметр2
Первый параметр это номер обновляемой версии 1С, если 0 то чистая установка
Второй параметр это номер новой версии 1С
Файл установки 1С должен находиться в одном каталоге со скриптом
Если указан номер обновляемой версии 1С, то должен существовать каталог установки обновляемой версии 1С
Пример: $PROGNAME 0 8.3.24.1586
Пример: $PROGNAME 8.3.23.1586 8.3.24.1586

Поскольку настроящий сисадмин должен быть не только умный, но и ленивый, не буду заставлять его ходить на github, а положу весь скрипт под кат. Но кто сходит, тот найдет еще пару скриптов: на 1С Исполнитель и bash для обновления только веб-публикаций для IIS и Apache
Лицензия MIT
#!/bin/bash # Copyright (c) 2026 [@capitan_nemo_spb] # # Данная лицензия разрешает лицам, получившим копию данного программного # обеспечения и сопутствующей документации (в дальнейшем именуемого "Программное # обеспечение"), безвозмездно использовать Программное обеспечение без ограничений, # включая неограниченное право на использование, копирование, изменение, слияние, # публикацию, распространение, сублицензирование и/или продажу копий Программного # обеспечения, а также лицам, которым предоставляется данное Программное # обеспечение, при соблюдении следующих условий: # # Указанное выше уведомление об авторских правах и данные условия лицензии должны # быть включены во все копии или значимые части Программного обеспечения. # # ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ "КАК ЕСТЬ", БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, # ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ # ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО НАЗНАЧЕНИЮ И НЕНАРУШЕНИЯ ПРАВ. НИ В КАКОМ СЛУЧАЕ # АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА # УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, ВОЗНИКШИМ В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ # ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ. set -euo pipefail PROGNAME="$(basename "$0")" SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" usage() { cat <<EOF Использование программы: $PROGNAME параметр1 параметр2 Первый параметр это номер обновляемой версии 1С, если 0 то чистая установка Второй параметр это номер новой версии 1С Файл установки 1С должен находиться в одном каталоге со скриптом Если указан номер обновляемой версии 1С, то должен существовать каталог установки Пример: $PROGNAME 0 8.3.24.1586 Пример: $PROGNAME 8.3.23.1586 8.3.24.1586 EOF } log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" } err() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] ОШИБКА: $*" >&2 } replace_version_in_file() { local target_file="$1" if [[ -f "$target_file" ]]; then perl -pE "s/\Q${old_version}\E/${new_version}/g" -i "$target_file" fi } http_replace_version() { local conf_file="$1" if grep -qF "${old_version}" "${conf_file}"; then log "Изменяется версия в файле веб-публикации: ${conf_file}" perl -i".bak_${current_date}" -pE "s/\Q${old_version}\E/${new_version}/g" "${conf_file}" fi } if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then usage exit 0 fi if [[ "$(id -u)" -ne 0 ]]; then err "Скрипт должен быть запущен с правами администратора" exit 1 fi if [[ $# -ne 2 ]]; then usage exit 1 fi old_version=$1 new_version=$2 current_date=$(date +'%Y%m%d_%H%M%S') bak_dir="${SCRIPT_DIR}/bak_${current_date}" log "Проверяется существование архива/файла установки" new_archive="${SCRIPT_DIR}/server64_${new_version//./_}.zip" shopt -s nullglob installer_matches=( "${SCRIPT_DIR}"/*-"${new_version}"-*.run ) shopt -u nullglob if [[ ${#installer_matches[@]} -eq 0 ]]; then if [[ -f "${new_archive}" ]]; then log "Разархивируется ${new_archive}..." unzip -o "${new_archive}" -d "${SCRIPT_DIR}" "*${new_version}*.run" shopt -s nullglob installer_matches=( "${SCRIPT_DIR}"/*-"${new_version}"-*.run ) shopt -u nullglob else err "Не обнаружен файл установки или архива новой версии 1С ${new_archive}" exit 1 fi fi if [[ ${#installer_matches[@]} -eq 0 || ! -f "${installer_matches[0]}" ]]; then err "Не обнаружен файл установки новой версии 1С" exit 1 fi new_file="${installer_matches[0]}" chmod +x "${new_file}" old_dir="/opt/1cv8/x86_64/${old_version}" new_dir="/opt/1cv8/x86_64/${new_version}" srv_suffixes=() ras_suffixes=() if [[ "${old_version}" != "0" ]]; then log "Старая версия не 0 (${old_version}), проверяется существование каталога установки" if [[ ! -f "${old_dir}/uninstaller-full" ]]; then err "Не обнаружен каталог обновляемой версии 1C ${old_version}" exit 1 fi mkdir -p "${bak_dir}" log "Сохраняются файлы старых служб с возможными нетиповыми настройками" # Поиск всех служб сервера 1С вида srv1cv8-OLD_VER@.service, srv1cv8-OLD_VER@2541.service и т.д. shopt -s nullglob old_srv_services=( "${old_dir}"/srv1cv8-"${old_version}"@*.service ) shopt -u nullglob if [[ ${#old_srv_services[@]} -eq 0 ]]; then log "Службы srv1cv8 не найдены в ${old_dir}, используется шаблон по умолчанию" srv_suffixes+=( "" ) else for srv_path in "${old_srv_services[@]}"; do srv_file="$(basename "${srv_path}")" # Извлекаем суффикс после @ и до .service (например: "" или "2541") suffix="${srv_file#srv1cv8-${old_version}@}" suffix="${suffix%.service}" srv_suffixes+=( "${suffix}" ) new_srv_name="srv1cv8-${new_version}@${suffix}.service" log "Резервное копирование и адаптация службы: ${srv_file} -> ${new_srv_name}" cp "${srv_path}" "${bak_dir}/${new_srv_name}" replace_version_in_file "${bak_dir}/${new_srv_name}" done fi # Поиск всех служб RAS вида ras-OLD_VER.service, ras-OLD_VER-2545.service, ras-OLD_VER@.service и т.д. shopt -s nullglob old_ras_services=( "${old_dir}"/ras-"${old_version}"*.service ) shopt -u nullglob if [[ ${#old_ras_services[@]} -eq 0 ]]; then log "Службы RAS не найдены в ${old_dir}" else for ras_path in "${old_ras_services[@]}"; do ras_file="$(basename "${ras_path}")" # Извлекаем суффикс после ras-${old_version} и до .service (например: "" для ras-8.3.23.service, "-2545" или "@2545") suffix="${ras_file#ras-${old_version}}" suffix="${suffix%.service}" ras_suffixes+=( "${suffix}" ) new_ras_name="ras-${new_version}${suffix}.service" log "Резервное копирование и адаптация службы RAS: ${ras_file} -> ${new_ras_name}" cp "${ras_path}" "${bak_dir}/${new_ras_name}" replace_version_in_file "${bak_dir}/${new_ras_name}" done fi log "Останавливается старая версия платформы 1С" for suffix in "${srv_suffixes[@]}"; do inst="default" [[ -n "${suffix}" ]] && inst="${suffix}" srv_unit="srv1cv8-${old_version}@${suffix}.service" srv_inst="srv1cv8-${old_version}@${inst}.service" systemctl stop "${srv_inst}" 2>/dev/null || true systemctl disable "${srv_inst}" 2>/dev/null || true for unit_link in \ "/etc/systemd/system/${srv_unit}" \ "/etc/systemd/system/${srv_inst}" \ "/etc/systemd/system/multi-user.target.wants/${srv_inst}" do if [[ -e "${unit_link}" || -L "${unit_link}" ]]; then rm -f "${unit_link}" fi done done log "Останавливаются старые службы RAS" for suffix in "${ras_suffixes[@]}"; do ras_unit="ras-${old_version}${suffix}.service" # Если служба шаблонная (содержит @), определяем имя инстанса if [[ "${suffix}" == *"@"* ]]; then inst_suffix="${suffix#*@}" [[ -z "${inst_suffix}" ]] && inst_suffix="default" ras_inst="ras-${old_version}@${inst_suffix}.service" else ras_inst="${ras_unit}" fi systemctl stop "${ras_inst}" 2>/dev/null || true systemctl disable "${ras_inst}" 2>/dev/null || true for unit_link in \ "/etc/systemd/system/${ras_unit}" \ "/etc/systemd/system/${ras_inst}" \ "/etc/systemd/system/multi-user.target.wants/${ras_inst}" do if [[ -e "${unit_link}" || -L "${unit_link}" ]]; then rm -f "${unit_link}" fi done done systemctl daemon-reload systemctl reset-failed "srv1cv8-*" "ras-*" 2>/dev/null || true else srv_suffixes+=( "" ) ras_suffixes+=( "" ) fi log "Производится установка новой версии платформы 1С (${new_version})..." "${new_file}" --mode unattended --enable-components "server,ws,server_admin,additional_admin_functions,liberica_jre,ru" log "Проверяется, что новая версия установилась..." new_service="${new_dir}/srv1cv8-${new_version}@.service" if [[ ! -f "${new_service}" ]]; then err "Некорректно установилась новая версия 1C: файл ${new_service} не найден" exit 1 fi if [[ "${old_version}" != "0" ]]; then log "Удаляется старая версия платформы 1С (${old_version})..." "${old_dir}/uninstaller-full" --mode unattended log "Восстановление конфигураций служб srv1cv8..." for suffix in "${srv_suffixes[@]}"; do target_srv="srv1cv8-${new_version}@${suffix}.service" bak_srv_file="${bak_dir}/${target_srv}" dest_srv_file="${new_dir}/${target_srv}" if [[ -f "${bak_srv_file}" ]]; then if [[ -f "${dest_srv_file}" ]]; then mv "${dest_srv_file}" "${bak_dir}/${target_srv}.dist" fi log "Копирование файла службы: ${target_srv} -> ${new_dir}/" cp "${bak_srv_file}" "${dest_srv_file}" fi done log "Восстановление конфигураций служб RAS..." for suffix in "${ras_suffixes[@]}"; do target_ras="ras-${new_version}${suffix}.service" bak_ras_file="${bak_dir}/${target_ras}" dest_ras_file="${new_dir}/${target_ras}" if [[ -f "${bak_ras_file}" ]]; then if [[ -f "${dest_ras_file}" ]]; then mv "${dest_ras_file}" "${bak_dir}/${target_ras}.dist" fi log "Копирование файла службы RAS: ${target_ras} -> ${new_dir}/" cp "${bak_ras_file}" "${dest_ras_file}" fi done fi log "Запускаются службы 1С..." systemctl daemon-reload for suffix in "${srv_suffixes[@]}"; do inst="default" [[ -n "${suffix}" ]] && inst="${suffix}" srv_unit_file="${new_dir}/srv1cv8-${new_version}@${suffix}.service" srv_inst_name="srv1cv8-${new_version}@${inst}" if [[ -f "${srv_unit_file}" ]]; then systemctl link "${srv_unit_file}" systemctl enable "${srv_inst_name}" --now else err "Файл службы ${srv_unit_file} не найден для регистрации!" fi done for suffix in "${ras_suffixes[@]}"; do ras_unit_file="${new_dir}/ras-${new_version}${suffix}.service" if [[ "${suffix}" == *"@"* ]]; then inst_suffix="${suffix#*@}" [[ -z "${inst_suffix}" ]] && inst_suffix="default" ras_inst_name="ras-${new_version}@${inst_suffix}" else ras_inst_name="ras-${new_version}${suffix}.service" fi if [[ -f "${ras_unit_file}" ]]; then systemctl link "${ras_unit_file}" systemctl enable "${ras_inst_name}" --now fi done log "Ожидание запуска служб..." for ((i=0; i<15; i++)); do all_active=true for suffix in "${srv_suffixes[@]}"; do inst="default" [[ -n "${suffix}" ]] && inst="${suffix}" if ! systemctl is-active --quiet "srv1cv8-${new_version}@${inst}"; then all_active=false break fi done if [[ "${all_active}" == "true" ]]; then for suffix in "${ras_suffixes[@]}"; do ras_unit_file="${new_dir}/ras-${new_version}${suffix}.service" [[ ! -f "${ras_unit_file}" ]] && continue if [[ "${suffix}" == *"@"* ]]; then inst_suffix="${suffix#*@}" [[ -z "${inst_suffix}" ]] && inst_suffix="default" ras_inst_name="ras-${new_version}@${inst_suffix}" else ras_inst_name="ras-${new_version}${suffix}.service" fi if ! systemctl is-active --quiet "${ras_inst_name}"; then all_active=false break fi done fi if [[ "${all_active}" == "true" ]]; then log "Все службы успешно перешли в активное состояние." break fi sleep 2 done for suffix in "${srv_suffixes[@]}"; do inst="default" [[ -n "${suffix}" ]] && inst="${suffix}" systemctl status "srv1cv8-${new_version}@${inst}" --no-pager -l || true done for suffix in "${ras_suffixes[@]}"; do ras_unit_file="${new_dir}/ras-${new_version}${suffix}.service" [[ ! -f "${ras_unit_file}" ]] && continue if [[ "${suffix}" == *"@"* ]]; then inst_suffix="${suffix#*@}" [[ -z "${inst_suffix}" ]] && inst_suffix="default" ras_inst_name="ras-${new_version}@${inst_suffix}" else ras_inst_name="ras-${new_version}${suffix}.service" fi systemctl status "${ras_inst_name}" --no-pager -l || true done if [[ "${old_version}" != "0" ]]; then web_conf_dirs=() [[ -d '/etc/httpd' ]] && web_conf_dirs+=( '/etc/httpd' ) [[ -d '/etc/apache2' ]] && web_conf_dirs+=( '/etc/apache2' ) if [[ ${#web_conf_dirs[@]} -gt 0 ]]; then for conf_dir in "${web_conf_dirs[@]}"; do readarray -d '' -t find_files < <(find "${conf_dir}" -name '*.conf' -size +1c -type f -print0) for file_path in "${find_files[@]}"; do http_replace_version "${file_path}" done done log "Перезапускается веб-сервер..." if systemctl list-unit-files 2>/dev/null | grep -qE '^(httpd|apache2)\.service'; then systemctl restart httpd 2>/dev/null || systemctl restart apache2 2>/dev/null || true fi fi fi log "Обновление/установка платформы 1С успешно завершена!"
Будет интересно, напишу про обновление клиентов.
Статья продолжает серию публикаций:
Графическая утилита для очистки кэша 1С (с возможностью выбора базы) для ОС Linux
Установка комьюнити-лицензии разработчика на сервер 1С. Рецепты от Капитана
Платформа 1C: Предприятие 8.3 в корпоративной сети РедОС. Тысяча мелочей
Бесплатный вебинар «В облаке, как дома: Как настроить рабочее место пользователя 1С на базе РЕД ОС»
Сервер 1С: Предприятие на Ubuntu 16.04 и PostgreSQL 9.6, для тех, кто хочет узнать его вкус. Рецепт от Капитана обратите внимание на дату публикации
Серия «Рецепты от Капитана» на всякий случай
VenbergV
Не увидел, как вы отключаете 1C Data Accelerator, при установке новой версии платформы.
capitannemo Автор
Не отключаем. Пока не было с ним проблем, кроме серверов с лицензией разработчика.
На них Требования назначения функциональности его отключают, они при переходе на новую платформу не теряются.
Совсем давно да, переименовывал на таких серверах файл службы
У вас выедает память или ошибки dmesg?