Скрипт 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С успешно завершена!"

Будет интересно, напишу про обновление клиентов.

Статья продолжает серию публикаций:

Серия «Рецепты от Капитана» на всякий случай

Комментарии (2)


  1. VenbergV
    21.10.2026 09:37

    Не увидел, как вы отключаете 1C Data Accelerator, при установке новой версии платформы.


    1. capitannemo Автор
      21.10.2026 09:37

      Не отключаем. Пока не было с ним проблем, кроме серверов с лицензией разработчика.

      На них Требования назначения функциональности его отключают, они при переходе на новую платформу не теряются.

      Совсем давно да, переименовывал на таких серверах файл службы

      У вас выедает память или ошибки dmesg?