if [ $? -ne 0 ]; then
echo "錯誤: 無法關閉現有 Tomcat 實例,終止啟動流程!" >&2
exit 1
fi
$?
?是shell中的特殊變量,表示上一個命令的退出狀態碼-ne 0
?表示"不等于0"(在Unix/Linux中,0通常表示成功,非0表示錯誤)- 如果前一個命令執行失敗(返回非0),則執行花括號內的內容:
- 輸出錯誤信息到標準錯誤輸出(stderr)
- 使用exit 1終止腳本執行并返回錯誤狀態碼1
--------------------------------------------------------------------------------------------------
if [ -z "$1" ]; then
echo "用法: $0 <TOMCAT_DIRECTORY>"
exit 1
fi
具體解釋如下:
if [ -z "$1" ]
?檢查第一個參數($1
)是否為空字符串-z
?測試字符串長度是否為0$1
?表示腳本的第一個參數
如果參數為空(即用戶沒有提供參數),則:
- 輸出用法提示:
echo "用法: $0 <TOMCAT_DIRECTORY>"
$0
?表示當前腳本的名稱
- 以狀態碼1退出腳本:
exit 1
?(1通常表示錯誤退出)
- 輸出用法提示:
---------------------------------------------------------------------------------------------------
TOMCAT_DIR="$1"
TIMEOUT=10
TOMCAT=$(basename "${TOMCAT_DIR}")
CURRENT_USER=$(whoami)
以下是詳細解釋:
TOMCAT_DIR="$1"
- 將腳本的第一個參數(
$1
)賦值給變量TOMCAT_DIR
- 這個參數應該是Tomcat的安裝目錄路徑
- 將腳本的第一個參數(
TIMEOUT=10
- 設置超時時間為10秒
- 用于后續等待Tomcat關閉的時間控制
TOMCAT=$(basename "${TOMCAT_DIR}")
- 使用
basename
命令從目錄路徑中提取最后一級目錄名 - 例如:如果
TOMCAT_DIR
是/opt/apache-tomcat-9.0.45
,則TOMCAT
值為apache-tomcat-9.0.45
- 使用
CURRENT_USER=$(whoami)
- 獲取當前執行腳本的用戶名
- 用于后續的權限檢查和日志記錄
-------------------------------------------------------------------------------------------------------------
# 檢查目錄和腳本存在性
[ ! -d "${TOMCAT_DIR}" ] && echo "錯誤: 目錄不存在" && exit 1
[ ! -x "${TOMCAT_DIR}/bin/shutdown.sh" ] && echo "錯誤: shutdown.sh 不可執行" && exit 1
# 獲取 Tomcat 主進程 PID(精確匹配)
get_pids() {
ps -ef | grep "${TOMCAT}" | grep "org.apache.catalina.startup.Bootstrap" | grep -v grep | awk '{print $2}'
}
詳細解釋它們的功能和實現原理:
目錄和腳本存在性檢查部分:
[ ! -d "${TOMCAT_DIR}" ]
:檢查指定的Tomcat目錄是否存在-d
測試目錄是否存在!
表示邏輯非
[ ! -x "${TOMCAT_DIR}/bin/shutdown.sh" ]
:檢查shutdown.sh腳本是否存在且可執行-x
測試文件是否存在且可執行
- 如果任一檢查失敗,會輸出錯誤信息并以狀態碼1退出
獲取Tomcat進程PID的函數:
ps -ef
:列出所有進程的完整信息grep "${TOMCAT}"
:過濾包含Tomcat目錄名的進程grep "org.apache.catalina.startup.Bootstrap"
:精確匹配Tomcat主類grep -v grep
:排除grep命令自身的進程awk '{print $2}'
:提取第二列(即PID)- 這個函數通過多級過濾確保只獲取真正的Tomcat主進程PID
----------------------------------------------------------------------------------------------------------------
# 初始檢查進程
PIDS=$(get_pids)
if [ -z "${PIDS}" ]; then
echo "Tomcat 未運行,無需關閉"
exit 0
fi
echo "檢測到 Tomcat 進程 (PID: ${PIDS})"
# 步驟1:嘗試優雅關閉(忽略非關鍵錯誤,但記錄)
echo "嘗試優雅關閉..."
if ! ${TOMCAT_DIR}/bin/shutdown.sh; then
echo "警告: shutdown.sh 執行失敗(可能權限不足)"
fi
# 等待優雅關閉
echo "等待 ${TIMEOUT} 秒..."
sleep ${TIMEOUT}
詳細解析:
初始進程檢查:
- 調用
get_pids
函數獲取Tomcat進程PID - 如果PID為空(
-z
測試),輸出提示并正常退出(exit 0) - 否則顯示檢測到的進程PID
- 調用
優雅關閉流程:
- 首先嘗試執行Tomcat的
shutdown.sh
腳本 - 使用
if ! command
結構捕獲執行失敗情況 - 即使失敗也僅輸出警告而不終止(因為后續還有強制關閉邏輯)
- 首先嘗試執行Tomcat的
等待處理:
- 使用
sleep
命令等待預設的TIMEOUT
時間(之前定義為10秒) - 這是給Tomcat完成正常關閉流程的時間窗口
- 使用
----------------------------------------------------------------------------------------------------------
# 步驟2:檢查是否仍有進程
PIDS=$(get_pids)
if [ -n "${PIDS}" ]; then
echo "嘗試正常終止進程 (PID: ${PIDS})"
# 逐個檢查并終止進程,記錄失敗
for pid in ${PIDS}; do
# 檢查進程所有者
PROCESS_OWNER=$(ps -o user= -p "${pid}" 2>/dev/null || echo "unknown")
echo "進程 ${pid} 所有者: ${PROCESS_OWNER}"
# 嘗試終止進程
if ! kill "${pid}" 2>/dev/null; then
echo "錯誤: 無法終止進程 ${pid}(權限不足,當前用戶 ${CURRENT_USER} 無法操作 ${PROCESS_OWNER} 的進程)"
exit 1 ?# 權限不足時立即報錯退出
fi
done
? ? # 等待后再次檢查
sleep 5
PIDS=$(get_pids)
if [ -n "${PIDS}" ]; then
echo "嘗試強制終止進程 (PID: ${PIDS})"
for pid in ${PIDS}; do
if ! kill -9 "${pid}" 2>/dev/null; then
echo "錯誤: 無法強制終止進程 ${pid}(權限不足)"
exit 1 ?# 權限不足時立即報錯退出
fi
done
fi
fi
# 最終檢查
PIDS=$(get_pids)
if [ -z "${PIDS}" ]; then
echo "Tomcat 已成功關閉"
else
echo "錯誤: 仍有進程未關閉 (PID: ${PIDS})"
exit 1
fi
echo "========== 關閉完成 =========="
exit 0
詳細解析:
進程二次檢查:
- 使用
-n
測試檢查是否仍有存活的Tomcat進程 - 如果有則進入強制終止流程
- 使用
分級終止策略:
- 先嘗試普通kill命令(SIGTERM信號)
- 檢查每個進程的所有者(使用
ps -o user= -p PID
) - 對kill失敗的情況立即報錯退出(權限問題)
強制終止階段:
- 等待5秒后再次檢查
- 對仍然存活的進程使用
kill -9
(SIGKILL信號) - 同樣處理權限錯誤情況
最終狀態確認:
- 最后一次檢查進程狀態
- 根據結果輸出成功/失敗信息
- 返回相應的退出狀態碼(0成功/1失敗)
這個設計體現了完善的進程管理策略:
- 分級處理(先優雅后強制)
- 完善的錯誤檢查和權限驗證
- 明確的進程狀態跟蹤
- 清晰的執行反饋
代碼中幾個關鍵Shell技巧:
2>/dev/null
屏蔽錯誤輸出ps -o user=
只輸出用戶名列|| echo "unknown"
錯誤處理- 多級
if [ -n/-z ]
條件測試