oracle_19c_ru_ojvm_upgrade.sh脚本的初始版本来源于IT邦德的分享,使用原脚本时发现有一些bug,在我的环境中脚本根本跑不通,于是个人在这个脚本的基础上进行了大量的改进与优化,到当前版本可以说算是完全重构了。我用这个脚本进行了大量的测试验证(测试环境和UAT环境Oracle 19c数据库实例打补丁),对于Oracle 19c来说基本上是没有什么问题. 使用此脚本打补丁相当丝滑与惬意。也确实让工作效率飞升,可以腾出很多时间做其他事情。脚本的一些思路与想法,阅读下面代码即可略知一二,如果有不足的地方,也敬请指教。当然,这个脚本暂时没有在其他数据库版本经过测试验证。难免会一些Bug,个人后续也会不断地完善、扩充这个脚本。关于这个脚本的一些基本注意事项,详情请见下面:

注意事项:

  1. 此脚本只在Linux(REHL)平台上测试了Oracle 19c数据库, 虽然经过大量验证,不保证其它平台环境也能运行,可能存在Bug,使用前请进行测试验证,作者不保证脚本没有任何bug

  2. 使用前,根据实际情况修改相关变量.

  3. REQUIRED_OPATCH_VERSION变量需要指定的opatch版本根据官方文档资料指定

  4. 脚本目前还只适用于单实例

  5. Oracle 19c早期版本需要先回滚OJVM补丁,然后安装新的补丁,从Oracle Database 19.17.0开始,官方发布了 RU + OJVM Combination Patch(组合补丁)。在该组合补丁中,OJVM 补丁已被整合进 RU 安装映像,不再需要先回滚旧的 OJVM 补丁. 脚本里面没有考虑早期版本升级需要先回滚OJVM补丁情况. 如有需要,请自行完善.

  6. 多实例环境,又分相同数据库版本和不同数据库版本,这里脚本暂未实现这个功能, 属实太复杂的场景会让脚本变得无比复杂,代码量继续飙增.这个脚本代码行数破千了。如果是相同数据库版本的多实例,只保留一个实例和监听服务,其它关闭,然后跑完脚本,最后在启动其它实例,只需跑datapatch脚本

  7. 脚本的函数prepare_run_sql只是特殊环境需要授权,应该很多人的数据库环境根本不需要这样的授权,可以注释删除这个函数。

#!/bin/bash##########################################################################################                                                                                       ## Oracle 19c RU + OJVM 一键升级脚本,此脚本初始版本来源于IT邦德,使用原脚本时发现有一些bug## 于是个人在其基础上进行了大量的调整与改进,这个脚本基本上可以算是完全重构了。          ###########################################################################################                                                                                       ## ScriptName            :    oracle_19c_ru_ojvm_upgrade.sh                              ## Author                :    潇湘隐者                                                   ## CerateDate            :    2025-08-21                                                 ## Email                 :    [email protected]                                       ##***************************************************************************************## 变量配置                                                                              ##---------------------------------------------------------------------------------------## CONNECT_INFO        连接数据库的方式,默认为系统认证模式(如需适用账号密码访问,调整即可 ## ORACLE_SID          ORACLE_SID                                                        ## ORACLE_HOME         ORACLE主目录                                                      ## PATCH_DIR           Oracle的补丁文件存放路径                                          ## OPATCH_PATCH        Oracle的opatch补丁文件名                                          ## REQUIRED_OPATCH_VERSION 要求的最低opatch版本                                          ## RU_PATCH            RU补丁文件名                                                      ## OJVM_PATCH          OJVM补丁名                                                        ##---------------------------------------------------------------------------------------## 参数说明                                                                              ##---------------------------------------------------------------------------------------##     此脚本无须使用参数                                                                ##---------------------------------------------------------------------------------------##  Usage:                                                                               ##          sh oracle_19c_ru_ojvm_upgrade.sh                                             ##      或  ./oracle_19c_ru_ojvm_upgrade.sh                                              ##***************************************************************************************## 注意事项:                                                                             ##    1. 此脚本只在Linux(REHL)平台上测试了Oracle 19c数据库, 虽然经过大量验证,不保证其它  ##       平台环境也能运行,可能存在Bug,使用前请进行测试验证,作者不保证脚本没有任何bug     ##    2. 使用前,根据实际情况修改相关变量.                                                ##    3. REQUIRED_OPATCH_VERSION变量需要指定的opatch版本根据官方文档资料指定             ##    4. 脚本目前还只适用于单实例                                                        ##    5. Oracle 19c早期版本需要先回滚OJVM补丁,然后安装新的补丁,从Oracle Database 19.17.0 ##       开始,官方发布了 RU + OJVM Combination Patch(组合补丁)。在该组合补丁中,OJVM 补丁##       已被整合进 RU 安装映像,不再需要先回滚旧的 OJVM 补丁. 脚本里面没有考虑早期版本升##       级需要先回滚OJVM补丁情况. 如有需要,请自行完善.                                  ##    6. 多实例环境,又分相同数据库版本和不同数据库版本,这里脚本暂未实现这个功能,         ##       属实太复杂的场景会让脚本变得无比复杂,代码量继续飙增.这个脚本代码行数破千了      ##       如果是相同数据库版本的多实例,只保留一个实例和监听服务,其它关闭,然后跑完脚本,最后##       在启动其它实例,只需跑datapatch脚本                                              ##***************************************************************************************## Version        Modified Date            Description                                   ##***************************************************************************************## V.0.0          2025-08-19              IT邦德的原始脚本                               ## V.1.0          2025-08-21              修改/创建此脚本                                ## V.1.1          2025-08-25              增加逻辑判断,fix掉几个bug                      ## V.1.2          2025-08-29              关闭/启动数据库实例,关闭/启动监听功能封装成函数##                                        独立出来,方便简单调用                          ## V.1.3          2025-09-01              打补丁前检查各个PDB失效对象信息,打完补丁后执行 ##                                        重编译无效对象                                 ## V.1.4          2025-09-02              打完补丁,检查补丁安装信息                      ## V.1.5          2025-09-03              完善部分功能与(19c非租户环境)执行检查无        ##                                        效对象出错的Bug                                ########################################################################################### =============== 安全控制 ===============# 严格错误处理#set -euo pipefail  #trap "echo 'ERROR: 脚本异常退出,请检查日志!'; exit 1" ERR# =============== 配置区(根据实际修改)===============# ORACLE_SIDexport ORACLE_SID="gsp"# ORACLE_HOME目录export ORACLE_HOME="/opt/oracle19c/product/19.3.0/db_1"# 数据库的连接方式,请根据实际情况调整readonly CONNECT_INFO="conn / as sysdba"# 数据库补丁存放的路径readonly PATCH_DIR="/data/soft"# OPATCH补丁文件名readonly OPATCH_PATCH="p6880880_190000_Linux-x86-64.zip"# 要求的最低opatch版本readonly REQUIRED_OPATCH_VERSION="12.2.0.1.46"# RU补丁文件名readonly RU_PATCH="p37960098_190000_Linux-x86-64.zip"# OJVM补丁文件名readonly OJVM_PATCH="p37847857_190000_Linux-x86-64.zip"# 下面变量基本无须修改ROLLBACK_FILE="${PATCH_DIR}/rollback_${ORACLE_SID}_$(date +%Y%m%d).sql"LOG_DATE=$(date +%Y%m%d%H%M)readonly SUCCESS=0readonly FAILURE=1LOG_FILE="${PATCH_DIR}/patch_${ORACLE_SID}_${LOG_DATE}.log"# Log输出方式:log或cmd或allLOG_OUT_TYPE=allLSNR_NAME=""DB_VERSION="19"IS_MULTI_DB=""OS_TYPE=""PDB_LIST=""# 记录脚本的日志信息输出log_info(){    #判断参数个数    if [ $# -eq 1 ];then        local log_msg=$1        case $LOG_OUT_TYPE in            cmd)                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                ;;            log)                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                ;;            all)                # log_info暂时不会发送邮件                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                ;;            *)        esac    elif [ $# -eq 2 ];then        local log_msg=$1        case $2 in            cmd)                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                ;;            log)                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                ;;             all)                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                echo -e "[info]: $(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                ;;            *)        esac    else        echo -e "[error]: $(date '+%Y%m%d %H:%M:%S')> the number of parameters is incorrect!"    fi}# 记录脚本的错误信息输出log_error(){    #判断参数个数    if [ $# -eq 1 ];then        local log_msg=$1        case $LOG_OUT_TYPE in            cmd)                 echo  -e  "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                 ;;            log)                 echo  -e  "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                 ;;            all)                 echo -e "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                 echo -e "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                 ;;            *)        esac    elif [ $# -eq 2 ];then        local log_msg=$1        case $2 in            cmd)                 echo  -e  "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                 ;;            log)                 echo  -e  "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                 ;;            all)                 echo -e "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}"                 echo -e "[error]:$(date '+%Y%m%d %H:%M:%S')> ${log_msg}" >> "$LOG_FILE"                 ;;            *)        esac    else        echo -e "[error]: $(date '+%Y%m%d %H:%M:%S')> the number of parameters is incorrect!"    fi}precheck() {    log_info "precheck开始预检..."        # 1. 检查操作系统    OS_TYPE=$(uname -a | awk ' { print $1} ')    log_info "当前操作系统为: "${OS_TYPE}" "       if [ "$OS_TYPE" == "Linux" ];    then         log_info "当前操作系统为 ${OS_TYPE},检查通过"    else        log_info "当前脚本没有在Linux之外平台测试过,请谨慎使用!"    fi         # 2. 运行脚本的当前用户检查/确认    if [ "$(whoami)" != "oracle" ];     then        log_error "必须使用oracle用户执行此脚本!"        exit ${FAILURE}    else        log_info "账号检查正常,当前账号为$(whoami)"    fi        # 3. Oracle补丁文件检查是否齐全    if [[ ! -f ${PATCH_DIR}/${RU_PATCH} || ! -f ${PATCH_DIR}/${OJVM_PATCH} || ! -f ${PATCH_DIR}/${OPATCH_PATCH} ]];     then        log_error "Oracle相关补丁文件缺失!,请检查补丁包文件是否齐全"        exit ${FAILURE}    else        log_info "Oracle安装升级的补丁文件齐全,如下所示:"        ls -lrt ${PATCH_DIR}/${RU_PATCH}        ls -lrt ${PATCH_DIR}/${OJVM_PATCH}        ls -lrt ${PATCH_DIR}/${OPATCH_PATCH}    fi        # 4. 数据库监听服务检查确认    local curr_lsn_num    curr_lsn_num=$(ps -e -o args | grep tnslsnr | grep -v grep |wc -l)    if [ "${curr_lsn_num}" -eq 0 ];    then        log_info "当前环境不存在监听服务或监听服务已经关闭了"        read -r  -p "请输入正确的监听服务名"  listener_name        LSNR_NAME=$(listener_name)     elif [ "${curr_lsn_num}" -eq 1 ]    then       LSNR_NAME=$(ps -e -o args | grep tnslsnr | grep -v grep | awk '{print $2}' | tr "[:upper:]" "[:lower:]")       log_info "当前监听名称为: ${LSNR_NAME}"    elif [ "${curr_lsn_num}" -gt 1 ];    then       log_info "当前环境有多个监听服务,请指定监听服务名: "       read -r  -p "请输入正确的监听服务名: "  listener_name       LSNR_NAME=$(listener_name)     fi         # 5. 数据库实例的状态检查确认    local curr_db_status    curr_db_status=$(check_db_status)            if [ "$curr_db_status" != "OPEN" ];     then        log_error "数据库已处于关闭状态,数据库必须处于OPEN状态"        exit  ${FAILURE}    else        IS_MULTI_DB=$(check_db_multitenant)        log_info "数据库状态为: ${curr_db_status} "    fi    # 5. OPatch版本检查    log_info "Opatch的版本信息如下所示:"    $ORACLE_HOME/OPatch/opatch version     # 6. 数据库版本信息    log_info "数据库的版本信息如下所示:"    sqlplus -S /nolog <<EOF        whenever sqlerror exit sql.sqlcode        ${CONNECT_INFO}        set linesize 120        select banner_full from v$version;        exit;EOF    # 7. 检查失效对象信息    log_info "数据库的失效对象检查."    check_invalid_obj        # 8. 是否继续补丁升级    read -r -n1 -p "Do you want to continue installing patches? please choose the [Y/N]?" answer        case $answer in            Y | y)                log_info "precheck预检结束"                ;;            N | n)                log_info "You have chosen to exit the patch installation."                exit ${FAILURE}                ;;            *)                log_error "your choice is wrong!"                exit ${FAILURE}                ;;        esac}check_db_version(){local curr_db_statuscurr_db_status=$(check_db_status)if [ "${curr_db_status}" == "DOWN" ];then    log_error "the oracle instance is down, please check it"else    db_version_sql=$(sqlplus -S /nolog  <<EOF |awk -F= "/^a=/ {print $2}" SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF${CONNECT_INFO}SELECT 'a='||REGEXP_SUBSTR(VERSION, '^d+') FROM V$INSTANCE;EXITEOF         )    if [[ -n "${db_version_sql}"  && "${db_version_sql}" -gt 9 ]];    then        DB_VERSION=${db_version_sql}    else        log_error "获取Oracle数据库版本出错,请检查脚本与日志!"    fi fi}# 检查数据库当前状态check_db_status() {    local status=""    local db_inst_num="0"    db_inst_num=$(ps -ef | grep ora_pmon_${ORACLE_SID} | grep -v grep | wc -l)    if [ "${db_inst_num}" -eq 0 ]; then       echo "DOWN"    else       status=$(       sqlplus -S /nolog <<EOF  | awk -F= "/^a=/ {print $2}"        set head off pagesize 0 feedback off linesize 400       whenever sqlerror exit 1       ${CONNECT_INFO}       select 'a='||status from v$instance;       exitEOF      )      echo "${status}"     fi        return "${SUCCESS}"}# 检查数据库是否多租户环境check_db_multitenant() {    check_db_version    if [ "${DB_VERSION}" -ge 12 ];    then         IS_PDB_EXIST=$(sqlplus  -S /nolog <<EOF        ${CONNECT_INFO}        SET HEADING OFF         WHENEVER SQLERROR EXIT SQL.SQLCODE        SELECT COUNT(*) VALUE FROM CDB_PDBS;        EXITEOF        )        if [ "$IS_PDB_EXIST" -ge 1 ];        then            echo "Y"        else            echo "N"        fi    else        echo "N"    fi}# 停止监听服务stop_listener() {if [ "$OS_TYPE" == "Linux" ] ; then     #LSNR_NAME=`ps -ef | grep tns | grep $ORACLE_HOME | grep -v grep | awk '{ print $9 }'`    LSNR_NAME=$(ps -e -o args | grep tnslsnr | grep -v grep | head -1 | awk '{print $2}' | tr "[:upper:]" "[:lower:]")elif [ "$OS_TYPE" == "AIX" ] ; then    #LSNR_NAME=`ps -ef | grep tns | grep $ORACLE_HOME | grep -v grep | awk '{ print $10 }'`    LSNR_NAME=$(ps -ef | grep tnslsnr | grep -v grep | awk '{print $10 }')elif [ "OS_TYPE" == "HP-UX"] ; then    LSNR_NAME=$(ps -ef | grep tnslsnr | grep -v grep | awk '{print $10 }')filocal curr_lsn_numcurr_lsn_num=$(ps -ef | grep tnslsnr | grep -v grep |wc -l)if [ "$curr_lsn_num" -eq 0 ];then    log_info "当前环境数据库监听服务已经停止或不存在, 请检查确认!"    exit ${FAILURE}elif [ "$curr_lsn_num" -eq 1 ];then    lsnrctl stop "$LSNR_NAME"elif [ "$curr_lsn_num" -gt 1 ];then    read -r -n1 -p "Do you like shutting down all listening services? please choose the [Y/N]?" answer        case $answer in            Y | y)                for lsnr_name_item in ${LSNR_NAME};                do                  lsnrctl stop "$lsnr_name_item"                done                ;;            N | n)                read -r  -p "Please specify the listener service to be stopped." curr_lsnr_name                lsnrctl stop "$curr_lsnr_name"                ;;            *)                log_error "your choice was wrong!"                exit ${FAILURE}                ;;        esacfi}# 启动监听服务start_listener() {    local curr_lsn_num    local curr_lsnr_name        if [ -z "${LSNR_NAME}" ]; then        log_error ""${LSNR_NAME}" is null, please check it"        read -r  -p "Please specify the listener service to be stopped." curr_lsnr_name                lsnrctl start "$curr_lsnr_name"                if [ $? -eq 0 ]; then            log_info  "Oracle监听服务${LSNR_NAME}启动成功"        else            log_error "Oracle监听服务${LSNR_NAME}启动失败"            exit ${FAILURE}        fi    else            curr_lsn_num=$(ps -ef | grep tnslsnr | grep "${LSNR_NAME}" | grep -v grep |wc -l)        if [ "$curr_lsn_num" -eq 0 ];        then            lsnrctl start  "${LSNR_NAME}"            if [ $? -eq 0 ]; then                log_info  "Oracle监听服务${LSNR_NAME}启动成功"            else                log_error "Oracle监听服务${LSNR_NAME}启动失败"                exit ${FAILURE}            fi        else            log_info "the listener ${LSNR_NAME} is started now"        fi    fi}shutdown_oracle() {    local curr_db_status    curr_db_status=$(check_db_status)    # 此处只适用于Linux,HP-UX环境会报错,如需适用于HP-UX的话,其用下面注释代码    #local curr_lsn_num=$(ps -ef | grep tnslsnr | grep -v grep |wc -l)    local curr_lsn_num    curr_lsn_num=$(ps -e -o args | grep tnslsnr | grep -v grep |wc -l)    if [ "${curr_lsn_num}" -gt 1 ];    then        log_info "当前环境中有多个监听,请新开一个窗口进行检查,并手工关闭这些监听服务."        read -r -n1 -p "数据库监听服务已经关闭了吗,请输入(Y或N)"  answer        case $answer in            Y | y)                log_info "数据库监听服务已经关闭!"                ;;            N | n)                log_info "You have chosen to exit the patch installation."                exit ${FAILURE}                ;;            *)                log_error "your choice is wrong!"                exit ${FAILURE}                ;;        esac    elif [ "${curr_lsn_num}" -eq 1 ];    then        LSNR_NAME=$(ps -e -o args | grep tnslsnr | grep -v grep | awk '{print $2}' | tr "[:upper:]" "[:lower:]")        log_info "Listener Name: ${LSNR_NAME}"        lsnrctl stop "${LSNR_NAME}"        if [ $? -eq 0 ];        then            log_info "stop the Listener  ${LSNR_NAME} successed."        else           log_error "stop the listener ${LSNR_NAME} failed ,please check the log"           exit ${FAILURE}        fi    else        log_info "the Listener ${LSNR_NAME} is stopped status. it does not need to stop it!"    fi            if [ "${curr_db_status}" == "DOWN" ];     then        log_info "数据库已处于关闭状态,无需关闭数据库实例!"        return  ${SUCCESS}    else         sqlplus -S /nolog <<EOF        whenever sqlerror exit sql.sqlcode        ${CONNECT_INFO}        shutdown immediate;        exit;EOF        if [ $? -eq 0 ];        then            log_info  "Oracle instance shutdown done.n"        else            log_error "Oracle instance shutdown failed.n"            exit ${FAILURE}        fi        INSTANCE_STATUS=$(check_db_status)                if [  "$INSTANCE_STATUS" == "DOWN" ];        then            log_info "Oracle instance is shutdown now"        else            log_error "the oracle instance status is ${INSTANCE_STATUS}, pleas check it "            exit $FAILURE        fi    fi}start_oracle() {        start_listener        curr_db_status=$(check_db_status)        if [ "$curr_db_status" == "DOWN" ];     then        if [ "${IS_MULTI_DB}" == "Y" ];        then              sqlplus -S /nolog <<EOFwhenever sqlerror exit sql.sqlcode${CONNECT_INFO}  startup;alter pluggable database all open;alter pluggable database all save state;  exit;EOF              if [ $? -eq 0 ]; then                  log_info  "Oracle数据库实例启动成功"              else                  log_error "Oracle数据库实例启动失败"                  exit ${FAILURE}              fi        else                sqlplus -S /nolog <<EOFwhenever sqlerror exit sql.sqlcode${CONNECT_INFO}  startup;exit;EOF              if [ $? -eq 0 ]; then                  log_info  "Oracle数据库实例${ORACLE_SID}启动成功"              else                  log_error "Oracle数据库实例${ORACLE_SID}启动失败"                  exit ${FAILURE}              fi        fi    fi    curr_db_status=$(check_db_status)    if [ "${curr_db_status}" != "OPEN" ]    then        log_error "Oracle数据库实例${ORACLE_SID}启动失败.n"         exit ${FAILURE}    else        log_info "Oracle数据库实例${ORACLE_SID}启动成功n"       fi}# 返回PDB数据库列表get_pdb_list() {PDB_LIST=$(sqlplus -S /nolog <<EOF${CONNECT_INFO}set pagesize 0 feedback off verify off heading off echo offselect 'CDB$ROOT' name from dual union allselect name from v$pdbs where open_mode = 'READ WRITE';exit;EOF)if [ $? -eq 0 ];then    return ${SUCCESS}else    return ${FAILURE}fi}# =============== 备份模块 ===============create_backup() {    log_info ">>> 创建回滚点..."    sqlplus -S / as sysdba <<EOF > "$ROLLBACK_FILE"set serveroutput onexec dbms_qopatch.get_sqlpatch_status;exit;EOF    log_info ">>> 备份重要文件:"    $ORACLE_HOME/OPatch/opatch lsinventory -detail > ${PATCH_DIR}/inventory_bak_"$(date +%s)".txt    #cp ${ORACLE_HOME}/sqlpatch/sqlpatch_bundle/* ${PATCH_DIR}/sqlpatch_bak_$(date +%s)/}# 版本比较函数(处理形如x.y.z.a的版本格式)version_ge() {    # 比较版本:如果$1 ≥ $2则返回0(成功),否则返回1    [ "$(printf "%sn%s" "$2" "$1" | sort -V | head -n1)" = "$2" ]}opatch_upgrade() {local -r patch_file=$1# 定义OPatch路径local -r OPATCH_CMD="${ORACLE_HOME}/OPatch/opatch"    if [ -f "${PATCH_DIR}/${patch_file}" ];then    unzip -o "${PATCH_DIR}/${patch_file}" -d ${PATCH_DIR}    PATCH_NAME=$(unzip -l "${PATCH_DIR}/${patch_file}" | awk '//$/ {print $4}' | cut -d'/' -f1 | sort -u)    cd "${PATCH_DIR}" || exit    if [ -d "${ORACLE_HOME}/OPatch" ];    then         # 备份旧的OPatch目录         mv "${ORACLE_HOME}/OPatch"  "${ORACLE_HOME}/OPatch.${LOG_DATE}"         cp -rp "${PATCH_DIR}/${PATCH_NAME}" "${ORACLE_HOME}/OPatch"    else        log_error "error, please check it"        exit ${FAILURE}    fi        # 检查opatch可执行文件是否存在    if [ ! -x "${OPATCH_CMD}" ]; then        log_error "错误:OPatch工具不存在或不可执行,路径:${OPATCH_CMD}"        exit ${FAILURE}    fi    # 获取当前OPatch版本    log_info "正在检查OPatch版本..."    CURRENT_OPATCH_VERSION=$("${OPATCH_CMD}" version | awk '/OPatch Version/ {print $3}')    if [ -z "${CURRENT_OPATCH_VERSION}" ]; then        log_error "错误:无法获取OPatch版本信息"        exit ${FAILURE}    fi    log_info "当前OPatch版本:${CURRENT_OPATCH_VERSION}"    log_info "要求的最低OPatch版本:${REQUIRED_OPATCH_VERSION}"    # 检查版本是否符合要求    if version_ge "${CURRENT_OPATCH_VERSION}" "${REQUIRED_OPATCH_VERSION}"; then        log_info "OPatch版本符合要求"        return $SUCCESS    else        log_error "OPatch版本不符合要求,请升级至${REQUIRED_OPATCH_VERSION}或更高版本"        exit ${FAILURE}    fi    "${ORACLE_HOME}"/OPatch/opatch version | grep -q "${OPATCH_VER}" || {    log_error "错误:OPatch版本不满足要求!"    exit ${FAILURE}  }fi    }# =============== 补丁应用模块 ===============apply_patch() {  local patch_file=$1  local patch_type=$2    log_info "应用${patch_type}补丁:$(basename "${patch_file}")"  unzip -o "${PATCH_DIR}/${patch_file}" -d "${PATCH_DIR}"  #PATCH_NAME=$(basename $patch_file .zip)  PATCH_NAME=$(unzip -l "${PATCH_DIR}/${patch_file}" | awk '//$/ {print $4}' | cut -d'/' -f1 | sort -u)  cd "${PATCH_DIR}/${PATCH_NAME}" || exit  "${ORACLE_HOME}"/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph .  "${ORACLE_HOME}"/OPatch/opatch apply -silent   if [ $? -eq 0 ];  then          log_info "Oracle RU patch  apply success"  else          log_error "Oracle RU patch  apply failed ,please check the log "          exit ${FAILURE}  fi}data_patch() {    start_oracle    $ORACLE_HOME/OPatch/datapatch -verbose        if [ $? -eq 0 ];    then        log_info "Oracle PUS patch success"    else        log_error "Oracle PUS patch failed ,please check the log "        exit ${FAILURE}    fi        }prepare_run_sql(){local -r sql_cmd_text="GRANT EXECUTE ON  HTTPURITYPE TO PUBLIC;"if [ "$IS_MULTI_DB" == "N" ];then    sqlplus -S /nolog <<EOF                   ${CONNECT_INFO}                    whenever sqlerror exit sql.sqlcode                    ${sql_cmd_text}                    exit;EOF       if [ $? -eq 0 ];       then           log_info "在$pdb_name中执行SQL结束..."       else            log_error "在$pdb_name中执行SQL: ${sql_cmd_text} 异常"            exit ${FAILURE}       fielse    if ! get_pdb_list; then        exit ${FAILURE}    fi         # 检查是否获取到PDB列表    if [ -z "$PDB_LIST" ]; then        log_error "错误: 未能获取到PDB列表,请检查数据库连接"        exit ${FAILURE}    fi        # 在每个PDB中执行SQL语句    for pdb_name in  ${PDB_LIST};    do        if [ -n "$pdb_name" ];         then            log_info "在$pdb_name中执行SQL开始..."            # 执行SQL语句            sqlplus -S /nolog <<EOF                            whenever sqlerror exit sql.sqlcode                            ${CONNECT_INFO}                            ALTER SESSION SET CONTAINER="$pdb_name";                            ${sql_cmd_text}                            exit;EOF                        if [ $? -eq 0 ];            then                log_info "在$pdb_name中执行SQL结束..."            else                log_error "在$pdb_name中执行SQL: ${sql_cmd_text} 异常"                exit ${FAILURE}            fi            fi    donefi}check_invalid_obj() {local sql_cmd_text=""if [ "$IS_MULTI_DB" == "Y" ];then        if ! get_pdb_list; then        exit ${FAILURE}    fi         # 检查是否获取到PDB列表    if [ -z "$PDB_LIST" ]; then        log_error "错误: 未能获取到PDB列表,请检查数据库连接"        exit ${FAILURE}    fi    # 在每个PDB中执行SQL语句    for pdb_name in  ${PDB_LIST};    do        if [ -n "$pdb_name" ];         then            sql_cmd_text="            ${CONNECT_INFO}            whenever sqlerror exit sql.sqlcode            set serveroutput on;            ALTER SESSION SET CONTAINER="$pdb_name";            set linesize 720            set pagesize 60            col object_name for a40            col object_type for a15            col owner for a10            select object_name,object_type,owner,status from dba_objects where status<>'VALID' order by owner,object_name;            exit;"            log_info "正在 $pdb_name 中执行SQL..."            # 执行SQL语句            sqlplus -S /nolog <<EOF             ${sql_cmd_text}EOF            if [ $? -eq 0 ];            then                log_info "$pdb_name 中检查无效对象完成."            else                log_error "$pdb_name 中检查无效对象异常."                exit $FAILURE            fi        fi    doneelse    sql_cmd_text="    ${CONNECT_INFO}    whenever sqlerror exit sql.sqlcode    set serveroutput on;    set linesize 720    set pagesize 60    col object_name for a40    col object_type for a15    col owner for a10    select object_name,object_type,owner,status from dba_objects where status<>'VALID' order by owner,object_name;    exit;"    log_info "正在 $pdb_name 中执行SQL..."    # 执行SQL语句    sqlplus -S /nolog <<EOF     ${sql_cmd_text}EOF    if [ $? -eq 0 ];    then        log_info "$pdb_name 中检查无效对象完成."    else        log_error "$pdb_name 中检查无效对象异常."        exit $FAILURE    fifi}recompile_invalid_obj() {local sql_cmd_text=""if [ "$IS_MULTI_DB" == "N" ];then    sql_cmd_text="${CONNECT_INFO}                  whenever sqlerror exit sql.sqlcode                  set serveroutput on;                  @?/rdbms/admin/utlrp.sql                  exit;"    # 执行SQL语句    sqlplus -S /nolog <<EOF    ${sql_cmd_text}EOF    if [ $? -eq 0 ];    then        log_info "在$pdb_name 中执行脚本utlrp.sql完成..."    else        log_error "在$pdb_name 中执行脚本utlrp.sql出现异常..."        exit ${FAILURE}    fielse    if ! get_pdb_list; then        exit ${FAILURE}    fi         # 检查是否获取到PDB列表    if [ -z "$PDB_LIST" ]; then        log_error "错误: 未能获取到PDB列表,请检查数据库连接"        exit ${FAILURE}    fi        # 在每个PDB中执行SQL语句    for pdb_name in  ${PDB_LIST};    do        if [ -n "$pdb_name" ];         then            log_info "正在$pdb_name中执行脚本utlrp.sql开始..."            sql_cmd_text="${CONNECT_INFO}                          whenever sqlerror exit sql.sqlcode                          set serveroutput on;                          ALTER SESSION SET CONTAINER="$pdb_name";                          @?/rdbms/admin/utlrp.sql                          exit;"            # 执行SQL语句            sqlplus -S /nolog <<EOF            ${sql_cmd_text}EOF             if [ $? -eq 0 ];            then                log_info "在$pdb_name 中执行脚本utlrp.sql完成..."            else                log_error "在$pdb_name 中执行脚本utlrp.sql出现异常..."                exit ${FAILURE}            fi            fi    donefi}check_patch_info() {    log_info "执行检查补丁信息如下所示:"            # 执行SQL语句    sqlplus -S /nolog <<EOF                    whenever sqlerror exit sql.sqlcode                    ${CONNECT_INFO}                    ALTER SESSION SET CONTAINER="$pdb_name";                    set serveroutput on;                    set linesize 640;                    set pagesize 40;                    column action_time for a19                    column action format a16                    column version format a26                    column id for 99                    column comments format a30                    column bundle_series format a10                    select to_char(action_time, 'yyyy-mm-dd hh24:mi:ss') as action_time                         , action                         , version                         , id                         , comments                         , bundle_series                     from sys.registry$history                    order by action_time;                                        set linesize 640;                    set serveroutput on;                    col description for a30;                        col action_time for a30 ;                       select patch_id, patch_uid,action, status,action_time,description from dba_registry_sqlpatch;                                        set linesize 720                    col version_full for a19                    col status for a10;                    col modified for a20;                    select version,version_full,status,modified                    from dba_registry;                    exit;EOF    if [ $? -ne 0 ];    then         log_error "执行检查补丁信息的SQL语句出现错误,请检查确认!"        exit ${FAILURE}    fi}main() {    #执行预检    precheck    # 特殊授权    prepare_run_sql     #创建备份    create_backup    #升级opatch    opatch_upgrade $OPATCH_PATCH    #关闭数据库实例    shutdown_oracle    #应用RU补丁    apply_patch $RU_PATCH "RU"    #应用OJVM补丁    apply_patch $OJVM_PATCH "OJVM"        #数据库data patch    data_patch    # 最终验证    log_info ">>> 验证补丁状态:"    $ORACLE_HOME/OPatch/opatch lspatches    $ORACLE_HOME/OPatch/opatch lsinventory | grep -E "${RU_PATCH%.*}|${OJVM_PATCH%.*}"        log_info ">>>>>> 升级成功!请执行健康检查脚本验证数据库状态 <<<<<<"    check_invalid_obj    recompile_invalid_obj    check_patch_info  }# 执行主函数main | tee -a "${LOG_FILE}"

微信公众号的文章保存时,部分代码经常会出现部分单词间的空格被"自动删除", 所以建议你从下面的原文或
百度网盘链接获取源代码

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]