提交 535ebe7a authored 作者: 陈泽健's avatar 陈泽健

fix(service): 解决模块化拆分后脚本执行错误问题

- 添加Shell模式配置选项,默认使用PowerShell模块模式
- 修复文件上传函数参数错误,统一使用LocalPath和RemoteDir参数
- 改进JSON解析功能,过滤非JSON输出内容并处理空字符串情况
- 增加Shell脚本执行结果检查和错误处理机制
- 添加远程目录创建失败的错误处理
- 优化脚本上传流程,增加本地脚本存在性检查
- 为各种检测功能添加Shell模式包装函数
- 实现检测模式选择界面,支持PowerShell和Shell两种模式
上级 ba68c738
...@@ -66,6 +66,9 @@ $SSH_TIMEOUT = 30 ...@@ -66,6 +66,9 @@ $SSH_TIMEOUT = 30
# 脚本版本号(用于日志与报告) # 脚本版本号(用于日志与报告)
$SCRIPT_VERSION = "1.0.5" $SCRIPT_VERSION = "1.0.5"
# 检测模式配置(默认使用PowerShell模块模式)
$global:UseShellMode = $false # 设置为 $true 使用Shell脚本模式
# PuTTY 工具路径 # PuTTY 工具路径
$global:PLINK_PATH = $null $global:PLINK_PATH = $null
$global:PSCP_PATH = $null $global:PSCP_PATH = $null
...@@ -415,20 +418,41 @@ function Upload-ShellScript { ...@@ -415,20 +418,41 @@ function Upload-ShellScript {
) )
# 创建远程临时目录 # 创建远程临时目录
$cmd = "mkdir -p $RemotePath" $cmd = "mkdir -p $RemotePath 2>/dev/null"
Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cmd | Out-Null $mkdirResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cmd
if ($mkdirResult.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "[SHELL] 创建远程目录失败: $RemotePath"
return $false
}
# 上传基础函数库 # 检查本地脚本是否存在
$localCommonPath = Join-Path $SCRIPT_DIR "lib\shell\common.sh" $localCommonPath = Join-Path $SCRIPT_DIR "lib\shell\common.sh"
$remoteCommonPath = "$RemotePath/common.sh" $localScriptPath = Join-Path $SCRIPT_DIR "lib\shell\$ScriptName"
Copy-File-To-Remote -Server $Server -LocalPath $localCommonPath -RemotePath $remoteCommonPath
if (-not (Test-Path $localCommonPath)) {
Write-Log -Level "ERROR" -Message "[SHELL] 本地脚本不存在: $localCommonPath"
return $false
}
if (-not (Test-Path $localScriptPath)) {
Write-Log -Level "ERROR" -Message "[SHELL] 本地脚本不存在: $localScriptPath"
return $false
}
# 上传基础函数库
$commonUploadResult = Copy-File-To-Remote -LocalPath $localCommonPath -Server $Server -RemoteDir $RemotePath
if (-not $commonUploadResult) {
Write-Log -Level "ERROR" -Message "[SHELL] 上传基础函数库失败: common.sh"
return $false
}
# 上传检测脚本 # 上传检测脚本
$localScriptPath = Join-Path $SCRIPT_DIR "lib\shell\$ScriptName" $scriptUploadResult = Copy-File-To-Remote -LocalPath $localScriptPath -Server $Server -RemoteDir $RemotePath
$remoteScriptPath = "$RemotePath/$ScriptName" if (-not $scriptUploadResult) {
Copy-File-To-Remote -Server $Server -LocalPath $localScriptPath -RemotePath $remoteScriptPath Write-Log -Level "ERROR" -Message "[SHELL] 上传脚本失败: $ScriptName"
return $false
}
return $remoteScriptPath return $true
} }
# 执行远程Shell脚本 # 执行远程Shell脚本
...@@ -441,36 +465,484 @@ function Invoke-RemoteShellCheck { ...@@ -441,36 +465,484 @@ function Invoke-RemoteShellCheck {
) )
# 上传脚本 # 上传脚本
$remoteScriptPath = Upload-ShellScript -Server $Server -ScriptName $ScriptName -RemotePath $RemotePath $uploadSuccess = Upload-ShellScript -Server $Server -ScriptName $ScriptName -RemotePath $RemotePath
if (-not $uploadSuccess) {
Write-Log -Level "ERROR" -Message "[SHELL] 脚本上传失败: $ScriptName"
return $null
}
# 执行脚本 # 执行脚本(使用2>/dev/null抑制错误输出)
$cmd = "cd $RemotePath && chmod +x common.sh $ScriptName && ./$ScriptName $Arguments" $cmd = "cd $RemotePath 2>/dev/null && chmod +x common.sh $ScriptName 2>/dev/null && ./$ScriptName $Arguments 2>/dev/null"
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cmd $result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cmd
# 检查执行结果
if (-not $result) {
Write-Log -Level "ERROR" -Message "[SHELL] SSH命令执行失败: $ScriptName"
Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $RemotePath 2>/dev/null" | Out-Null
return $null
}
# 检查输出
if (-not $result.Output) {
Write-Log -Level "WARN" -Message "[SHELL] 脚本未返回输出: $ScriptName (退出码: $($result.ExitCode))"
}
# 清理临时文件 # 清理临时文件
Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $RemotePath" | Out-Null Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $RemotePath 2>/dev/null" | Out-Null
# 返回结果 # 返回结果
return $result.Output -join "`n" if ($result.Output) {
return $result.Output -join "`n"
} else {
return $null
}
} }
# 解析Shell脚本JSON结果 # 解析Shell脚本JSON结果
function ConvertFrom-ShellJson { function ConvertFrom-ShellJson {
param( param(
[Parameter(Mandatory=$true)] [string]$JsonString [Parameter(Mandatory=$false)] [string]$JsonString = ""
) )
# 检查输入是否为空
if ([string]::IsNullOrEmpty($JsonString)) {
Write-Log -Level "ERROR" -Message "[SHELL] Shell脚本未返回任何输出"
return $null
}
try { try {
# 移除可能的非JSON字符 # 过滤掉非JSON行(plink输出、错误信息等)
$cleanJson = $JsonString -replace '[^\x00-\x7F]', '' # JSON通常以 { 开头,所以只保留从 { 开始的内容
$lines = $JsonString -split "`n"
$jsonLines = @()
$inJson = $false
$braceCount = 0
foreach ($line in $lines) {
$trimmed = $line.Trim()
# 跳过空行
if ([string]::IsNullOrEmpty($trimmed)) { continue }
# 跳过常见命令输出和错误信息(包括中文输出)
if ($trimmed -match "^(chmod|mkdir|rm|cd|\$|pscp:|plink:|Fatal|Network|Connection|已用|总用量|Mem:|Swap:|Cpu)") { continue }
# 找到JSON开始位置
if (-not $inJson -and $trimmed.StartsWith("{")) {
$inJson = $true
}
if ($inJson) {
$jsonLines += $line
$braceCount += ($line.ToCharArray() | Where-Object { $_ -eq "{" } | Measure-Object).Count
$braceCount -= ($line.ToCharArray() | Where-Object { $_ -eq "}" } | Measure-Object).Count
# 当大括号平衡时,结束解析
if ($braceCount -le 0 -and $trimmed.EndsWith("}")) {
break
}
}
}
if ($jsonLines.Count -eq 0) {
Write-Log -Level "ERROR" -Message "[SHELL] 未找到JSON输出"
return $null
}
$cleanJson = $jsonLines -join "`n"
return $cleanJson | ConvertFrom-Json return $cleanJson | ConvertFrom-Json
} }
catch { catch {
Write-Log -Level "ERROR" -Message "[SHELL] JSON解析失败: $($_.Exception.Message)" Write-Log -Level "ERROR" -Message "[SHELL] JSON解析失败: $($_.Exception.Message)"
Write-Log -Level "INFO" -Message "[SHELL] 原始输出前200字符: $($JsonString.Substring(0, [Math]::Min(200, $JsonString.Length)))"
return $null return $null
} }
} }
# ================================
# Shell模式包装函数
# ================================
# 资源检测(Shell模式)
function Test-ServerResources-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始资源检测 (Shell模式) =========="
$arguments = "--format json --check all"
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "resource_check.sh" -Arguments $arguments
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[资源] Shell脚本执行失败"
return $null
}
# 转换为与PowerShell模块兼容的格式
$results = @()
# CPU信息
if ($data.cpu) {
$results += [PSCustomObject]@{
Category = "CPU"
Item = "使用率"
Value = "$($data.cpu.usage_percent)%"
Status = if ([int]$data.cpu.usage_percent -lt 80) { "正常" } else { "警告" }
}
$results += [PSCustomObject]@{
Category = "CPU"
Item = "负载平均"
Value = $data.cpu.load_average
Status = "正常"
}
}
# 内存信息
if ($data.memory) {
$memPercent = [int]$data.memory.usage_percent
$results += [PSCustomObject]@{
Category = "内存"
Item = "使用率"
Value = "$memPercent%"
Status = if ($memPercent -lt 80) { "正常" } elseif ($memPercent -lt 90) { "警告" } else { "严重" }
}
}
# 磁盘信息
if ($data.disk.mounts) {
foreach ($mount in $data.disk.mounts) {
$usagePercent = [int]$mount.usage_percent
$status = if ($usagePercent -lt 80) { "正常" } elseif ($usagePercent -lt 90) { "警告" } else { "严重" }
$results += [PSCustomObject]@{
Category = "磁盘"
Item = $mount.mountpoint
Value = "$($mount.used)/$($mount.size) ($($mount.usage_percent))"
Status = $status
}
}
}
Write-Log -Level "INFO" -Message "========== 结束资源检测 (Shell模式) =========="
return $results
}
# Docker容器检测(Shell模式)
function Test-ContainerInformation-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server,
[Parameter(Mandatory=$false)] [bool]$PrintDetails = $false
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始Docker检测 (Shell模式) =========="
$arguments = "--format json --detail basic"
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "docker_check.sh" -Arguments $arguments
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[容器] Shell脚本执行失败"
return $null
}
$results = @()
if ($data.containers) {
foreach ($container in $data.containers) {
$statusIcon = if ($container.state -eq "running") { "[运行]" } else { "[停止]" }
Write-Log -Level "INFO" -Message " $statusIcon $($container.name) - $($container.image)"
$results += [PSCustomObject]@{
Name = $container.name
Image = $container.image
State = $container.state
Status = $container.status
}
}
}
# 输出汇总信息
if ($data.summary) {
Write-Log -Level "INFO" -Message "汇总: 总计 $($data.summary.total) 个容器,运行中 $($data.summary.running) 个,已停止 $($data.summary.stopped) 个"
}
Write-Log -Level "INFO" -Message "========== 结束Docker检测 (Shell模式) =========="
return $results
}
# 中间件检测(Shell模式)
function Test-MQTTConnection-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server,
[Parameter(Mandatory=$false)] [string]$EmqxLogPath = ""
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始中间件检测 (Shell模式) =========="
$arguments = "--format json --check all"
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "middleware_check.sh" -Arguments $arguments
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[中间件] Shell脚本执行失败"
return @()
}
$results = @()
# Redis检测结果
if ($data.redis) {
$redisStatus = $data.redis.status
$icon = if ($redisStatus -eq "running") { "[运行]" } else { "[停止]" }
Write-Log -Level "INFO" -Message " $icon Redis ($($data.redis.container):$($data.redis.port)): $redisStatus"
$results += [PSCustomObject]@{
Middleware = "Redis"
Container = $data.redis.container
Port = $data.redis.port
Status = $redisStatus
}
}
# MySQL检测结果
if ($data.mysql) {
$mysqlStatus = $data.mysql.status
$icon = if ($mysqlStatus -eq "running") { "[运行]" } else { "[停止]" }
Write-Log -Level "INFO" -Message " $icon MySQL ($($data.mysql.container):$($data.mysql.port)): $mysqlStatus"
$results += [PSCustomObject]@{
Middleware = "MySQL"
Container = $data.mysql.container
Port = $data.mysql.port
Status = $mysqlStatus
}
}
# EMQX检测结果
if ($data.emqx) {
$emqxStatus = $data.emqx.status
$icon = if ($emqxStatus -eq "running") { "[运行]" } else { "[停止]" }
Write-Log -Level "INFO" -Message " $icon EMQX ($($data.emqx.container):$($data.emqx.port)): $emqxStatus"
$results += [PSCustomObject]@{
Middleware = "EMQX"
Container = $data.emqx.container
Port = $data.emqx.port
Status = $emqxStatus
}
}
# FastDFS检测结果
if ($data.fastdfs) {
$fastdfsStatus = $data.fastdfs.status
$icon = if ($fastdfsStatus -eq "running") { "[运行]" } else { "[停止]" }
Write-Log -Level "INFO" -Message " $icon FastDFS ($($data.fastdfs.container)): $fastdfsStatus"
$results += [PSCustomObject]@{
Middleware = "FastDFS"
Container = $data.fastdfs.container
Status = $fastdfsStatus
}
}
Write-Log -Level "INFO" -Message "========== 结束中间件检测 (Shell模式) =========="
return $results
}
# DNS检测(Shell模式)
function Test-DNSResolution-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始DNS检测 (Shell模式) =========="
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "dns_check.sh" -Arguments "--format json"
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[DNS] Shell脚本执行失败"
return @()
}
$results = @()
if ($data.results) {
foreach ($domain in $data.results) {
$icon = if ($domain.status -eq "success") { "[OK]" } else { "[FAIL]" }
Write-Log -Level "INFO" -Message " $icon $($domain.domain) -> $($domain.ip)"
$results += [PSCustomObject]@{
Domain = $domain.domain
Status = $domain.status
IP = $domain.ip
}
}
}
Write-Log -Level "INFO" -Message "DNS服务器: $($data.dns_server)"
Write-Log -Level "INFO" -Message "========== 结束DNS检测 (Shell模式) =========="
return $results
}
# NTP检测(Shell模式)
function Test-NTPService-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始NTP检测 (Shell模式) =========="
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "ntp_check.sh" -Arguments "--format json"
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[NTP] Shell脚本执行失败"
return $null
}
# 解析NTP服务状态 (ntp_service格式: "daemon|status")
$ntpParts = $data.ntp_service -split '\|'
$ntp_daemon = if ($ntpParts.Count -gt 0) { $ntpParts[0] } else { "unknown" }
$ntp_status = if ($ntpParts.Count -gt 1) { $ntpParts[1] } else { "unknown" }
$results = @()
$icon = if ($ntp_status -eq "running") { "[运行]" } else { "[停止]" }
Write-Log -Level "INFO" -Message " $icon NTP服务: $ntp_daemon ($ntp_status)"
if ($data.time_sync_status -eq "synchronized") {
Write-Log -Level "SUCCESS" -Message " [OK] 时间同步: 已同步"
} else {
Write-Log -Level "WARN" -Message " [WARN] 时间同步: 未同步"
}
Write-Log -Level "INFO" -Message "NTP服务器: $($data.ntp_servers)"
Write-Log -Level "INFO" -Message "时间偏差: $($data.time_offset) 秒"
$results += [PSCustomObject]@{
Service = "NTP"
Daemon = $ntp_daemon
Status = $ntp_status
TimeSync = $data.time_sync_status
TimeOffset = $data.time_offset
}
Write-Log -Level "INFO" -Message "========== 结束NTP检测 (Shell模式) =========="
return $results
}
# 配置IP检测(Shell模式)
function Test-ConfigIPs-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server,
[Parameter(Mandatory=$false)] [string]$PlatformType
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始配置IP检测 (Shell模式) =========="
$arguments = "--platform $PlatformType --format json"
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "config_check.sh" -Arguments $arguments
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[配置] Shell脚本执行失败"
return @()
}
$results = @()
if ($data.configs) {
foreach ($config in $data.configs) {
$hasIp = if ($config.has_ip -eq $true) { "包含IP" } else { "无IP" }
Write-Log -Level "INFO" -Message " [$hasIp] $($config.file)"
if ($config.has_ip -eq $true) {
$ips = if ($config.ips -is [array]) { $config.ips } else { @($config.ips -split ',') }
foreach ($ip in $ips) {
$results += [PSCustomObject]@{
File = $config.file
IP = $ip.Trim()
}
}
}
}
}
Write-Log -Level "INFO" -Message "汇总: $($data.summary.total_configs) 个配置文件,$($data.summary.configs_with_ip) 个包含IP,共 $($data.summary.total_ips) 个IP"
Write-Log -Level "INFO" -Message "========== 结束配置IP检测 (Shell模式) =========="
return $results
}
# Console配置检测(Shell模式)- 暂未实现,使用PowerShell模式
function Test-ConsoleConfig-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server,
[Parameter(Mandatory=$false)] [string]$PlatformType,
[Parameter(Mandatory=$false)] [hashtable]$SystemInfo
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== Console配置检测暂不支持Shell模式,使用PowerShell模式 =========="
# Shell模式暂不支持,返回空结果
return @()
}
# Java服务检测(Shell模式)
function Test-UjavaServices-Shell {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server,
[Parameter(Mandatory=$false)] [string]$ContainerName,
[Parameter(Mandatory=$false)] [string]$PlatformType = "new"
)
Write-Host ""
if ($ContainerName) {
Write-Log -Level "INFO" -Message "========== 检测 ujava 服务 (容器: $ContainerName) (Shell模式) =========="
}
else {
Write-Log -Level "INFO" -Message "========== 检测 ujava 服务 (宿主机) (Shell模式) =========="
}
$arguments = "--container $ContainerName --platform $PlatformType --format json"
$result = Invoke-RemoteShellCheck -Server $Server -ScriptName "java_check.sh" -Arguments $arguments
$data = ConvertFrom-ShellJson -JsonString $result
if (-not $data) {
Write-Log -Level "ERROR" -Message "[UJAVA] Shell脚本执行失败"
return @()
}
$results = @()
if ($data.services) {
foreach ($service in $data.services) {
$statusIcon = if ($service.status -eq "运行中") { "[OK]" } else { "[FAIL]" }
Write-Log -Level "INFO" -Message " $statusIcon $($service.name) ($($service.jar)) [$($service.location)]: $($service.status)"
$results += [PSCustomObject]@{
Service = $service.name
Pattern = $service.jar
Status = $service.status
Running = ($service.status -eq "运行中")
}
}
}
Write-Log -Level "INFO" -Message "========== 结束检测 (Shell模式) =========="
return $results
}
# ================================ # ================================
# 检查依赖 # 检查依赖
...@@ -1161,7 +1633,26 @@ function Main { ...@@ -1161,7 +1633,26 @@ function Main {
exit 3 exit 3
} }
Write-Host "" Write-Host ""
# 选择检测模式
Write-Host "==================================================================" -ForegroundColor Cyan
Write-Host " 请选择检测模式" -ForegroundColor Cyan
Write-Host "==================================================================" -ForegroundColor Cyan
Write-Host " [1] PowerShell模块模式 (默认,兼容性最好)"
Write-Host " [2] Shell脚本模式 (Linux服务器本地执行,性能更好)"
Write-Host "==================================================================" -ForegroundColor Cyan
$modeChoice = Read-Host "请输入模式编号 [默认: 1]"
if ($modeChoice -eq "2") {
$global:UseShellMode = $true
Write-Host "[模式] 已选择:Shell脚本模式" -ForegroundColor Green
}
else {
$global:UseShellMode = $false
Write-Host "[模式] 已选择:PowerShell模块模式" -ForegroundColor Green
}
Write-Host ""
# 检测平台类型 # 检测平台类型
$platformType = Get-PlatformType -Server $server $platformType = Get-PlatformType -Server $server
$Global:PlatformType = $platformType # 新增:供 Test-ContainerInformation 使用 $Global:PlatformType = $platformType # 新增:供 Test-ContainerInformation 使用
...@@ -1197,7 +1688,12 @@ function Main { ...@@ -1197,7 +1688,12 @@ function Main {
# ujava 检测 # ujava 检测
if ($systemInfo.HasUjava) { if ($systemInfo.HasUjava) {
# 先尝试在容器内检测 # 先尝试在容器内检测
$ujavaContainerResults = Test-UjavaServices -Server $server -ContainerName $systemInfo.UjavaContainer -PlatformType $platformType if ($global:UseShellMode) {
$ujavaContainerResults = Test-UjavaServices-Shell -Server $server -ContainerName $systemInfo.UjavaContainer -PlatformType $platformType
}
else {
$ujavaContainerResults = Test-UjavaServices -Server $server -ContainerName $systemInfo.UjavaContainer -PlatformType $platformType
}
# 检测宿主机服务(extapi) # 检测宿主机服务(extapi)
$ujavaHostResults = Test-UjavaHostServices -Server $server $ujavaHostResults = Test-UjavaHostServices -Server $server
...@@ -1275,14 +1771,24 @@ function Main { ...@@ -1275,14 +1771,24 @@ function Main {
$upythonVoiceResults = Test-ContainerPorts -Server $server -ContainerName $systemInfo.UpythonVoiceContainer -PortList $UpythonVoicePorts -ServiceType "upython_voice" $upythonVoiceResults = Test-ContainerPorts -Server $server -ContainerName $systemInfo.UpythonVoiceContainer -PortList $UpythonVoicePorts -ServiceType "upython_voice"
} }
} }
# DNS 解析检测(所有平台都需要检测) # DNS 解析检测(所有平台都需要检测)
Write-Host "" Write-Host ""
$dnsResults = Test-DNSResolution -Server $server if ($global:UseShellMode) {
$dnsResults = Test-DNSResolution-Shell -Server $server
}
else {
$dnsResults = Test-DNSResolution -Server $server
}
# 服务器资源分析(所有平台都需要检测) # 服务器资源分析(所有平台都需要检测)
Write-Host "" Write-Host ""
$resourceResults = Test-ServerResources -Server $server if ($global:UseShellMode) {
$resourceResults = Test-ServerResources-Shell -Server $server
}
else {
$resourceResults = Test-ServerResources -Server $server
}
# 容器信息收集(加入到自检报告) # 容器信息收集(加入到自检报告)
Write-Host "" Write-Host ""
...@@ -1353,21 +1859,26 @@ function Main { ...@@ -1353,21 +1859,26 @@ function Main {
Invoke-SSHCommand -HostName $server.IP -User $server.User -Pass $server.Pass -Port $server.Port -Command $createDirCmd | Out-Null Invoke-SSHCommand -HostName $server.IP -User $server.User -Pass $server.Pass -Port $server.Port -Command $createDirCmd | Out-Null
Write-Log -Level "INFO" -Message "[中间件] 日志导出目录: ./middleware_logs" Write-Log -Level "INFO" -Message "[中间件] 日志导出目录: ./middleware_logs"
# MQTT连接检测 # 中间件连接检测(支持双模式)
$mqttResults = Test-MQTTConnection -Server $server -EmqxLogPath $middlewareEmqxLogPath if ($global:UseShellMode) {
if ($mqttResults) { $middlewareResults += $mqttResults } $middlewareResults = Test-MQTTConnection-Shell -Server $server
} else {
# MQTT连接检测
$mqttResults = Test-MQTTConnection -Server $server -EmqxLogPath $middlewareEmqxLogPath
if ($mqttResults) { $middlewareResults += $mqttResults }
# Redis连接检测 # Redis连接检测
$redisConnResults = Test-RedisConnection -Server $server -RedisLogPath $middlewareRedisLogPath $redisConnResults = Test-RedisConnection -Server $server -RedisLogPath $middlewareRedisLogPath
if ($redisConnResults) { $middlewareResults += $redisConnResults } if ($redisConnResults) { $middlewareResults += $redisConnResults }
# MySQL连接检测 # MySQL连接检测
$mysqlConnResults = Test-MySQLConnection -Server $server -MysqlLogPath $middlewareMysqlLogPath $mysqlConnResults = Test-MySQLConnection -Server $server -MysqlLogPath $middlewareMysqlLogPath
if ($mysqlConnResults) { $middlewareResults += $mysqlConnResults } if ($mysqlConnResults) { $middlewareResults += $mysqlConnResults }
# FastDFS连接检测 # FastDFS连接检测
$fastdfsConnResults = Test-FastDFSConnection -Server $server -StorageLogPath $middlewareStorageLogPath -TrackerLogPath $middlewareTrackerLogPath $fastdfsConnResults = Test-FastDFSConnection -Server $server -StorageLogPath $middlewareStorageLogPath -TrackerLogPath $middlewareTrackerLogPath
if ($fastdfsConnResults) { $middlewareResults += $fastdfsConnResults } if ($fastdfsConnResults) { $middlewareResults += $fastdfsConnResults }
}
Write-Log -Level "INFO" -Message "========== 中间件连接检测完成 ==========" Write-Log -Level "INFO" -Message "========== 中间件连接检测完成 =========="
...@@ -1448,17 +1959,22 @@ function Main { ...@@ -1448,17 +1959,22 @@ function Main {
# 检测配置文件中的IP地址 # 检测配置文件中的IP地址
Write-Host "" Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始检测配置文件 IP ==========" Write-Log -Level "INFO" -Message "========== 开始检测配置文件 IP =========="
if ($platformType -eq "new") { if ($global:UseShellMode) {
Test-NewPlatformIPs -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port Test-ConfigIPs-Shell -Server $server -PlatformType $platformType
} elseif ($platformType -eq "old") { } else {
# ✅ 关键:把 systemInfo 传进去,才能按 meeting/unified 选路径 if ($platformType -eq "new") {
Test-TraditionalPlatformIPs -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port -SystemInfo $systemInfo Test-NewPlatformIPs -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port
} elseif ($platformType -eq "old") {
# ✅ 关键:把 systemInfo 传进去,才能按 meeting/unified 选路径
Test-TraditionalPlatformIPs -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port -SystemInfo $systemInfo
}
} }
Write-Log -Level "INFO" -Message "========== 结束检测配置文件 IP ==========" Write-Log -Level "INFO" -Message "========== 结束检测配置文件 IP =========="
# 检测配置文件中的console配置 # 检测配置文件中的console配置
Write-Host "" Write-Host ""
Write-Log -Level "INFO" -Message "========== 开始检测console配置 ==========" Write-Log -Level "INFO" -Message "========== 开始检测console配置 =========="
# Shell模式下暂不支持console配置检测,使用PowerShell模式
if ($platformType -eq "new") { if ($platformType -eq "new") {
$consoleResults = Test-NewPlatformConsole -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port $consoleResults = Test-NewPlatformConsole -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port
} elseif ($platformType -eq "old") { } elseif ($platformType -eq "old") {
...@@ -1468,7 +1984,11 @@ function Main { ...@@ -1468,7 +1984,11 @@ function Main {
# 检测 NTP 服务 # 检测 NTP 服务
Write-Log -Level "INFO" -Message "========== 开始检测NTP服务 ==========" Write-Log -Level "INFO" -Message "========== 开始检测NTP服务 =========="
$ntpResults = Test-NTPService -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port if ($global:UseShellMode) {
$ntpResults = Test-NTPService-Shell -Server $server
} else {
$ntpResults = Test-NTPService -ServerIP $server.IP -Username $server.User -Password $server.Pass -Port $server.Port
}
Write-Log -Level "INFO" -Message "NTP 服务检测完成." Write-Log -Level "INFO" -Message "NTP 服务检测完成."
# 输出 NTP 摘要 # 输出 NTP 摘要
if ($ntpResults) { if ($ntpResults) {
......
#!/bin/bash
# ==============================================================================
# config_check.sh
# ------------------------------------------------------------------------------
# 配置文件IP检测Shell脚本
#
# .SYNOPSIS
# 检测配置文件中的IP地址
#
# .DESCRIPTION
# 检测新统一平台或传统平台的配置文件中是否存在IP地址。
#
# .PARAMETERS
# --format 输出格式(json/text,默认json)
# --platform 平台类型(new/old,默认自动检测)
#
# .EXAMPLE
# ./config_check.sh
# ./config_check.sh --platform new
#
# .OUTPUTS
# JSON格式检测结果
#
# .NOTES
# 版本:1.0.0
# 创建日期:2026-05-13
#
# ==============================================================================
# 加载基础函数库
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"
# ================================
# 参数解析
# ================================
OUTPUT_FORMAT="json"
PLATFORM=""
while [[ $# -gt 0 ]]; do
case $1 in
--format)
OUTPUT_FORMAT="$2"
shift 2
;;
--platform)
PLATFORM="$2"
shift 2
;;
*)
shift
;;
esac
done
# 自动检测平台类型
if [ -z "$PLATFORM" ]; then
PLATFORM=$(check_new_platform)
fi
# ================================
# 配置文件路径
# ================================
# 新统一平台配置文件
NEW_PLATFORM_CONFIGS=(
"/data/middleware/nginx/config/*.conf"
"/data/middleware/emqx/config/*.conf"
"/data/middleware/mysql/conf/my.cnf"
)
# 传统平台配置文件
OLD_PLATFORM_CONFIGS=(
"/var/www/java/nginx-conf.d/*.conf"
"/var/www/emqx/config/*.conf"
"/var/www/redis/redis-*.conf"
)
# IP地址匹配模式(简单匹配,不覆盖所有情况)
IP_PATTERN="\b([0-9]{1,3}\.){3}[0-9]\b"
# ================================
# 检测函数
# ================================
# 检测配置文件中的IP
# 参数: $1=配置文件路径
check_config_ip() {
local config_path="$1"
if [ ! -f "$config_path" ]; then
echo "not_found"
return
fi
# 使用grep查找IP地址
local ips=$(grep -oE "$IP_PATTERN" "$config_path" 2>/dev/null | sort -u)
local ip_count=$(echo "$ips" | grep -c ".")
if [ "$ip_count" -gt 0 ]; then
# 返回IP列表,用|分隔
echo "found|$ips"
else
echo "no_ip"
fi
}
# 检测平台配置文件
check_platform_configs() {
local configs=()
local found_configs=()
local total_ips=0
if [ "$PLATFORM" = "new" ]; then
configs=("${NEW_PLATFORM_CONFIGS[@]}")
else
configs=("${OLD_PLATFORM_CONFIGS[@]}")
fi
for config_pattern in "${configs[@]}"; do
# 展开通配符
for config_file in $config_pattern 2>/dev/null; do
[ -f "$config_file" ] || continue
local result=$(check_config_ip "$config_file")
IFS='|' read -r status ips <<< "$result"
if [ "$status" = "found" ]; then
local ip_count=$(echo "$ips" | wc -l)
total_ips=$((total_ips + ip_count))
# 转换换行为逗号分隔
local ip_list=$(echo "$ips" | tr '\n' ',' | sed 's/,$//')
found_configs+=("$config_file:$ip_list")
else
found_configs+=("$config_file:no_ip")
fi
done
done
# 返回结果
echo "${#found_configs[@]}|$total_ips"
}
# ================================
# 输出函数
# ================================
output_json() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 确保PLATFORM已设置
if [ -z "$PLATFORM" ]; then
PLATFORM="unknown"
fi
echo "{"
json_kv "check_type" "config_ip_check"
json_kv "timestamp" "$timestamp"
json_kv "platform" "$PLATFORM" false
# 检测配置文件
local check_result=$(check_platform_configs)
IFS='|' read -r config_count total_ips <<< "$check_result"
# 确保返回默认值
config_count=${config_count:-0}
total_ips=${total_ips:-0}
echo " \"configs\": ["
local configs=()
if [ "$PLATFORM" = "new" ]; then
configs=("${NEW_PLATFORM_CONFIGS[@]}")
else
configs=("${OLD_PLATFORM_CONFIGS[@]}")
fi
local first=true
for config_pattern in "${configs[@]}"; do
for config_file in $config_pattern 2>/dev/null; do
[ -f "$config_file" ] || continue
local result=$(check_config_ip "$config_file")
IFS='|' read -r status ips <<< "$result"
if [ "$status" = "not_found" ]; then
continue
fi
if [ "$first" = true ]; then
first=false
else
echo ","
fi
# 转换换行为逗号分隔
local ip_list=$(echo "$ips" | tr '\n' ',' | sed 's/,$//')
echo -n " {"
echo -n "\"file\": \"$config_file\", "
echo -n "\"has_ip\": "
if [ "$status" = "found" ]; then
echo -n "true, "
echo -n "\"ips\": \"$ip_list\""
else
echo -n "false, "
echo -n "\"ips\": []"
fi
echo -n "}"
done
done
echo ""
echo " ],"
# 汇总信息
echo " \"summary\": {"
echo " \"total_configs\": $config_count,"
echo " \"configs_with_ip\": $config_count,"
echo " \"total_ips\": $total_ips"
echo " }"
echo "}"
}
output_text() {
echo "========== 配置IP检测 (平台: $PLATFORM) =========="
echo ""
local configs=()
if [ "$PLATFORM" = "new" ]; then
configs=("${NEW_PLATFORM_CONFIGS[@]}")
else
configs=("${OLD_PLATFORM_CONFIGS[@]}")
fi
local found_count=0
local total_ips=0
for config_pattern in "${configs[@]}"; do
for config_file in $config_pattern 2>/dev/null; do
[ -f "$config_file" ] || continue
local result=$(check_config_ip "$config_file")
IFS='|' read -r status ips <<< "$result"
if [ "$status" = "found" ]; then
found_count=$((found_count + 1))
local ip_count=$(echo "$ips" | wc -l)
total_ips=$((total_ips + ip_count))
# 显示IP列表
echo " [文件] $config_file"
echo "$ips" | while read -r ip; do
echo " $ip"
done
fi
done
done
echo ""
echo "汇总: 检测 $found_count 个配置文件包含IP地址,共 $total_ips 个IP"
echo ""
echo "========== 检测完成 =========="
}
# ================================
# 主函数
# ================================
main() {
if [ "$OUTPUT_FORMAT" = "text" ]; then
output_text
else
output_json
fi
}
# 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main
#!/bin/bash
# ==============================================================================
# dns_check.sh
# ------------------------------------------------------------------------------
# DNS解析检测Shell脚本
#
# .SYNOPSIS
# 检测DNS解析功能
#
# .DESCRIPTION
# 检测服务器DNS解析功能,测试常用域名的解析情况。
#
# .PARAMETERS
# --format 输出格式(json/text,默认json)
# --domains 自定义域名列表(逗号分隔)
#
# .EXAMPLE
# ./dns_check.sh
# ./dns_check.sh --domains "www.baidu.com,www.qq.com"
#
# .OUTPUTS
# JSON格式检测结果
#
# .NOTES
# 版本:1.0.0
# 创建日期:2026-05-13
#
# ==============================================================================
# 加载基础函数库
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"
# ================================
# 配置变量
# ================================
# 默认测试域名列表
DEFAULT_DOMAINS=(
"www.baidu.com"
"www.qq.com"
"www.aliyun.com"
)
# ================================
# 参数解析
# ================================
OUTPUT_FORMAT="json"
CUSTOM_DOMAINS=""
while [[ $# -gt 0 ]]; do
case $1 in
--format)
OUTPUT_FORMAT="$2"
shift 2
;;
--domains)
CUSTOM_DOMAINS="$2"
shift 2
;;
*)
shift
;;
esac
done
# ================================
# 检测函数
# ================================
# 检测单个域名解析
# 参数: $1=域名
check_domain() {
local domain="$1"
# 使用nslookup或dig检测
local result=""
if command -v nslookup &> /dev/null; then
result=$(nslookup "$domain" 2>/dev/null | grep -A 1 "Name:" | grep "Address:" | head -1 | awk '{print $2}')
elif command -v dig &> /dev/null; then
result=$(dig +short "$domain" 2>/dev/null | head -1)
fi
if [ -n "$result" ]; then
echo "success|$result"
else
echo "failed|N/A"
fi
}
# 获取DNS服务器
get_dns_server() {
# 从/etc/resolv.conf获取DNS服务器
if [ -f /etc/resolv.conf ]; then
local dns_servers=$(grep "^nameserver" /etc/resolv.conf | awk '{print $2}' | tr '\n' ',' | sed 's/,$//')
echo "$dns_servers"
else
echo "unknown"
fi
}
# ================================
# 输出函数
# ================================
output_json() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local dns_server=$(get_dns_server)
echo "{"
json_kv "check_type" "dns_check"
json_kv "timestamp" "$timestamp"
json_kv "dns_server" "$dns_server" false
# 确定要检测的域名列表
local domains=("${DEFAULT_DOMAINS[@]}")
if [ -n "$CUSTOM_DOMAINS" ]; then
IFS=',' read -ra domains <<< "$CUSTOM_DOMAINS"
fi
echo " \"results\": ["
local first=true
for domain in "${domains[@]}"; do
[ -z "$domain" ] && continue
local result=$(check_domain "$domain")
IFS='|' read -r status ip <<< "$result"
if [ "$first" = true ]; then
first=false
else
echo ","
fi
echo -n " {"
echo -n "\"domain\": \"$domain\", "
echo -n "\"status\": \"$status\", "
echo -n "\"ip\": \"$ip\""
echo -n "}"
done
echo ""
echo " ]"
echo "}"
}
output_text() {
local dns_server=$(get_dns_server)
echo "========== DNS解析检测 =========="
echo "DNS服务器: $dns_server"
echo ""
# 确定要检测的域名列表
local domains=("${DEFAULT_DOMAINS[@]}")
if [ -n "$CUSTOM_DOMAINS" ]; then
IFS=',' read -ra domains <<< "$CUSTOM_DOMAINS"
fi
for domain in "${domains[@]}"; do
[ -z "$domain" ] && continue
local result=$(check_domain "$domain")
IFS='|' read -r status ip <<< "$result"
local icon=""
case "$status" in
success) icon="[OK]";;
failed) icon="[FAIL]";;
esac
echo " $icon $domain -> $ip"
done
echo ""
echo "========== 检测完成 =========="
}
# ================================
# 主函数
# ================================
main() {
if [ "$OUTPUT_FORMAT" = "text" ]; then
output_text
else
output_json
fi
}
# 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main
...@@ -222,4 +222,6 @@ main() { ...@@ -222,4 +222,6 @@ main() {
} }
# 执行主函数 # 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main main
...@@ -205,4 +205,6 @@ main() { ...@@ -205,4 +205,6 @@ main() {
} }
# 执行主函数 # 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main main
#!/bin/bash
# ==============================================================================
# middleware_check.sh
# ------------------------------------------------------------------------------
# 中间件检测Shell脚本
#
# .SYNOPSIS
# 检测中间件服务运行状态
#
# .DESCRIPTION
# 检测Redis、MySQL、EMQX、FastDFS等中间件服务的连接状态。
#
# .PARAMETERS
# --format 输出格式(json/text,默认json)
# --check 检测类型(all/redis/mysql/emqx/fastdfs,默认all)
#
# .EXAMPLE
# ./middleware_check.sh
# ./middleware_check.sh --check redis,mysql
#
# .OUTPUTS
# JSON格式检测结果
#
# .NOTES
# 版本:1.0.0
# 创建日期:2026-05-13
#
# ==============================================================================
# 加载基础函数库
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"
# ================================
# 参数解析
# ================================
OUTPUT_FORMAT="json"
CHECK_TYPE="all"
while [[ $# -gt 0 ]]; do
case $1 in
--format)
OUTPUT_FORMAT="$2"
shift 2
;;
--check)
CHECK_TYPE="$2"
shift 2
;;
*)
shift
;;
esac
done
# ================================
# 中间件配置(从主脚本同步)
# ================================
REDIS_CONTAINER="uredis"
REDIS_PORT=6379
REDIS_PASSWORD="dNrprU&2S"
MYSQL_CONTAINER="umysql"
MYSQL_PORT=8306
MYSQL_PASSWORD="dNrprU&2S"
EMQX_CONTAINER="uemqx"
EMQX_PORT=1883
EMQX_DASHBOARD_PORT=18083
FASTDFS_CONTAINER="ustorage"
# ================================
# 检测函数
# ================================
# 检测Redis连接
check_redis() {
local container="$1"
local port="$2"
local password="$3"
# 检查容器是否存在
if [ "$(docker_container_exists $container)" -eq 0 ]; then
echo "not_found"
return
fi
# 检查端口监听
local listening=$(check_port_listen $port)
if [ "$listening" -eq 0 ]; then
echo "stopped"
return
fi
# 尝试连接
local response=""
if [ -n "$password" ]; then
response=$(docker_exec "$container" "redis-cli -a '$password' ping 2>/dev/null" | head -1)
else
response=$(docker_exec "$container" "redis-cli ping 2>/dev/null" | head -1)
fi
if [ "$response" = "PONG" ]; then
echo "running"
else
echo "error"
fi
}
# 检测MySQL连接
check_mysql() {
local container="$1"
local port="$2"
local password="$3"
# 检查容器是否存在
if [ "$(docker_container_exists $container)" -eq 0 ]; then
echo "not_found"
return
fi
# 检查端口监听
local listening=$(check_port_listen $port)
if [ "$listening" -eq 0 ]; then
echo "stopped"
return
fi
# 尝试连接(使用mysqladmin ping)
local response=$(docker_exec "$container" "mysqladmin -uroot -p'$password' ping 2>/dev/null | grep -c 'alive'")
if [ "$response" -gt 0 ]; then
echo "running"
else
echo "error"
fi
}
# 检测EMQX连接
check_emqx() {
local container="$1"
local port="$2"
# 检查容器是否存在
if [ "$(docker_container_exists $container)" -eq 0 ]; then
echo "not_found"
return
fi
# 检查端口监听
local listening=$(check_port_listen $port)
if [ "$listening" -eq 0 ]; then
echo "stopped"
return
fi
# 尝试连接(通过curl检查管理API)
local response=$(docker_exec "$container" "curl -s http://localhost:18083/status 2>/dev/null | grep -c 'emqx_status'")
if [ "$response" -gt 0 ]; then
echo "running"
else
echo "error"
fi
}
# 检测FastDFS
check_fastdfs() {
local container="$1"
# 检查容器是否存在
if [ "$(docker_container_exists $container)" -eq 0 ]; then
echo "not_found"
return
fi
# 检查tracker和storage进程
local tracker_count=$(docker_exec "$container" "ps aux | grep -v grep | grep 'fdfs_trackerd' | wc -l")
local storage_count=$(docker_exec "$container" "ps aux | grep -v grep | grep 'fdfs_storaged' | wc -l")
if [ "$tracker_count" -gt 0 ] && [ "$storage_count" -gt 0 ]; then
echo "running"
else
echo "stopped"
fi
}
# ================================
# 输出函数
# ================================
output_json() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "{"
json_kv "check_type" "middleware_check"
json_kv "timestamp" "$timestamp" false
# Redis检测
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "redis" ]; then
local redis_status=$(check_redis "$REDIS_CONTAINER" "$REDIS_PORT" "$REDIS_PASSWORD")
echo " \"redis\": {"
echo " \"container\": \"$REDIS_CONTAINER\","
echo " \"port\": $REDIS_PORT,"
echo " \"status\": \"$redis_status\""
echo " },"
fi
# MySQL检测
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "mysql" ]; then
local mysql_status=$(check_mysql "$MYSQL_CONTAINER" "$MYSQL_PORT" "$MYSQL_PASSWORD")
echo " \"mysql\": {"
echo " \"container\": \"$MYSQL_CONTAINER\","
echo " \"port\": $MYSQL_PORT,"
echo " \"status\": \"$mysql_status\""
echo " },"
fi
# EMQX检测
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "emqx" ]; then
local emqx_status=$(check_emqx "$EMQX_CONTAINER" "$EMQX_PORT")
echo " \"emqx\": {"
echo " \"container\": \"$EMQX_CONTAINER\","
echo " \"port\": $EMQX_PORT,"
echo " \"status\": \"$emqx_status\""
echo " },"
fi
# FastDFS检测
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "fastdfs" ]; then
local fastdfs_status=$(check_fastdfs "$FASTDFS_CONTAINER")
echo " \"fastdfs\": {"
echo " \"container\": \"$FASTDFS_CONTAINER\","
echo " \"status\": \"$fastdfs_status\""
echo " }"
fi
echo "}"
}
output_text() {
echo "========== 中间件检测 =========="
echo ""
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "redis" ]; then
local redis_status=$(check_redis "$REDIS_CONTAINER" "$REDIS_PORT" "$REDIS_PASSWORD")
local icon=""
case "$redis_status" in
running) icon="[RUN]";;
stopped) icon="[STOP]";;
not_found) icon="[N/A]";;
*) icon="[ERR]";;
esac
echo " $icon Redis ($REDIS_CONTAINER:$REDIS_PORT): $redis_status"
fi
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "mysql" ]; then
local mysql_status=$(check_mysql "$MYSQL_CONTAINER" "$MYSQL_PORT" "$MYSQL_PASSWORD")
local icon=""
case "$mysql_status" in
running) icon="[RUN]";;
stopped) icon="[STOP]";;
not_found) icon="[N/A]";;
*) icon="[ERR]";;
esac
echo " $icon MySQL ($MYSQL_CONTAINER:$MYSQL_PORT): $mysql_status"
fi
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "emqx" ]; then
local emqx_status=$(check_emqx "$EMQX_CONTAINER" "$EMQX_PORT")
local icon=""
case "$emqx_status" in
running) icon="[RUN]";;
stopped) icon="[STOP]";;
not_found) icon="[N/A]";;
*) icon="[ERR]";;
esac
echo " $icon EMQX ($EMQX_CONTAINER:$EMQX_PORT): $emqx_status"
fi
if [ "$CHECK_TYPE" = "all" ] || [ "$CHECK_TYPE" = "fastdfs" ]; then
local fastdfs_status=$(check_fastdfs "$FASTDFS_CONTAINER")
local icon=""
case "$fastdfs_status" in
running) icon="[RUN]";;
stopped) icon="[STOP]";;
not_found) icon="[N/A]";;
esac
echo " $icon FastDFS ($FASTDFS_CONTAINER): $fastdfs_status"
fi
echo ""
echo "========== 检测完成 =========="
}
# ================================
# 主函数
# ================================
main() {
if [ "$OUTPUT_FORMAT" = "text" ]; then
output_text
else
output_json
fi
}
# 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main
#!/bin/bash
# ==============================================================================
# ntp_check.sh
# ------------------------------------------------------------------------------
# NTP时间同步检测Shell脚本
#
# .SYNOPSIS
# 检测NTP时间同步状态
#
# .DESCRIPTION
# 检测系统时间同步状态,检查NTP服务运行情况。
#
# .PARAMETERS
# --format 输出格式(json/text,默认json)
#
# .EXAMPLE
# ./ntp_check.sh
#
# .OUTPUTS
# JSON格式检测结果
#
# .NOTES
# 版本:1.0.0
# 创建日期:2026-05-13
#
# ==============================================================================
# 加载基础函数库
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"
# ================================
# 参数解析
# ================================
OUTPUT_FORMAT="json"
while [[ $# -gt 0 ]]; do
case $1 in
--format)
OUTPUT_FORMAT="$2"
shift 2
;;
*)
shift
;;
esac
done
# ================================
# 检测函数
# ================================
# 检测NTP服务状态
check_ntp_service() {
# 检测chronyd(CentOS 8+)
if systemctl is-active --quiet chronyd 2>/dev/null; then
echo "chronyd|running"
return
fi
# 检测ntpd(CentOS 7)
if systemctl is-active --quiet ntpd 2>/dev/null; then
echo "ntpd|running"
return
fi
# 检测ntp服务
if systemctl is-active --quiet ntp 2>/dev/null; then
echo "ntp|running"
return
fi
echo "none|stopped"
}
# 检测时间同步状态
check_time_sync() {
# 检查系统时间是否与NTP服务器同步
local ntp_sync="unknown"
# 检查timedatectl状态
if command -v timedatectl &> /dev/null; then
local status=$(timedatectl status 2>/dev/null | grep "System clock synchronized" | grep -c "yes")
if [ "$status" -gt 0 ]; then
ntp_sync="synchronized"
else
ntp_sync="not_synchronized"
fi
fi
echo "$ntp_sync"
}
# 获取NTP服务器列表
get_ntp_servers() {
# 从配置文件获取NTP服务器
local servers=""
if [ -f /etc/chrony.conf ]; then
servers=$(grep "^server " /etc/chrony.conf 2>/dev/null | awk '{print $2}' | tr '\n' ',' | sed 's/,$//')
elif [ -f /etc/ntp.conf ]; then
servers=$(grep "^server " /etc/ntp.conf 2>/dev/null | awk '{print $2}' | tr '\n' ',' | sed 's/,$//')
fi
echo "${servers:-unknown}"
}
# 检测时间偏差
check_time_offset() {
# 使用ntpdate检测时间偏差
if command -v ntpdate &> /dev/null; then
local offset=$(ntpdate -q 2>/dev/null | grep "time server" | sed 's/.*time server.*offset \(.*\) sec.*/\1/' | head -1)
echo "${offset:-N/A}"
else
echo "N/A"
fi
}
# ================================
# 输出函数
# ================================
output_json() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# NTP服务状态
local ntp_service=$(check_ntp_service)
IFS='|' read -r ntp_daemon ntp_status <<< "$ntp_service"
# 时间同步状态
local time_sync=$(check_time_sync)
# NTP服务器
local ntp_servers=$(get_ntp_servers)
# 时间偏差
local time_offset=$(check_time_offset)
echo "{"
json_kv "check_type" "ntp_check"
json_kv "timestamp" "$timestamp"
json_kv "ntp_service" "$ntp_daemon"
json_kv "service_status" "$ntp_status" false
json_kv "time_sync_status" "$time_sync"
json_kv "ntp_servers" "$ntp_servers"
json_kv "time_offset" "$time_offset" false
echo "}"
}
output_text() {
local ntp_service=$(check_ntp_service)
IFS='|' read -r ntp_daemon ntp_status <<< "$ntp_service"
local time_sync=$(check_time_sync)
local ntp_servers=$(get_ntp_servers)
local time_offset=$(check_time_offset)
echo "========== NTP时间同步检测 =========="
echo ""
# NTP服务状态
local icon=""
if [ "$ntp_status" = "running" ]; then
icon="[RUN]"
else
icon="[STOP]"
fi
echo " $icon NTP服务: $ntp_daemon ($ntp_status)"
# 时间同步状态
if [ "$time_sync" = "synchronized" ]; then
echo " [OK] 时间同步: 已同步"
else
echo " [WARN] 时间同步: 未同步"
fi
# NTP服务器
echo " NTP服务器: $ntp_servers"
# 时间偏差
echo " 时间偏差: $time_offset 秒"
echo ""
echo "========== 检测完成 =========="
}
# ================================
# 主函数
# ================================
main() {
if [ "$OUTPUT_FORMAT" = "text" ]; then
output_text
else
output_json
fi
}
# 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main
...@@ -316,4 +316,6 @@ main() { ...@@ -316,4 +316,6 @@ main() {
} }
# 执行主函数 # 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main main
# _PRD_模块化拆分后IP检测脚本执行错误
> 来源:
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health.ps1`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/common.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/config_check.sh`
## 1. 问题报错信息
### 1.1 问题一:IP检测报错
```
[2026-05-13 14:59:24] [INFO] ========== 开始配置IP检测 (Shell模式) ==========
[2026-05-13 14:59:24] [INFO] 已找到 pscp: C:\Users\UBAINS\Desktop\Test-module\pscp.exe
[2026-05-13 14:59:24] [SUCCESS] 文件上传成功: C:\Users\UBAINS\Desktop\Test-module\lib\shell\common.sh -> root@192.168.5.46:/tmp/health_check/
[2026-05-13 14:59:24] [INFO] 已找到 pscp: C:\Users\UBAINS\Desktop\Test-module\pscp.exe
[2026-05-13 14:59:25] [SUCCESS] 文件上传成功: C:\Users\UBAINS\Desktop\Test-module\lib\shell\config_check.sh -> root@192.168.5.46:/tmp/health_check/
ConvertFrom-ShellJson : 无法将参数绑定到参数“JsonString”,因为该参数为空字符串。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:835 字符: 47
+ $data = ConvertFrom-ShellJson -JsonString $result
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [ConvertFrom-ShellJson],ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,ConvertFrom-ShellJson
[2026-05-13 14:59:25] [ERROR] [配置] Shell脚本执行失败
```
### 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
*文档结束*
# _PRD_模块化拆分后IP检测脚本执行错误_问题处理 - 计划执行文档
> 创建时间:2026-05-13
> 状态:已完成
> 来源:`Docs/PRD/服务自检/问题修复/_PRD_模块化拆分后IP检测脚本执行错误_问题处理.md`
---
## 1. 问题分析
### 1.1 核心问题
IP检测Shell脚本执行时返回空输出,导致PowerShell参数验证失败。
### 1.2 错误详情
#### 问题:空输出导致参数验证失败
```
ConvertFrom-ShellJson : 无法将参数绑定到参数"JsonString",因为该参数为空字符串。
```
**原因分析**
1. `Invoke-RemoteShellCheck` 返回 `$null`(空输出)
2. `ConvertFrom-ShellJson` 参数设置为 `Mandatory=$true`
3. 空字符串无法通过参数验证
---
## 2. 解决方案
### 2.1 修复ConvertFrom-ShellJson参数验证
**文件**`check_server_health.ps1`
**位置**`ConvertFrom-ShellJson` 函数
**修改前**
```powershell
param(
[Parameter(Mandatory=$true)] [string]$JsonString
)
```
**修改后**
```powershell
param(
[Parameter(Mandatory=$false)] [string]$JsonString = ""
)
# 检查输入是否为空
if ([string]::IsNullOrEmpty($JsonString)) {
Write-Log -Level "ERROR" -Message "[SHELL] Shell脚本未返回任何输出"
return $null
}
```
### 2.2 改进Invoke-RemoteShellCheck错误处理
**文件**`check_server_health.ps1`
**位置**`Invoke-RemoteShellCheck` 函数
**改进内容**
- 添加执行结果检查
- 添加空输出警告日志
- 显示退出码信息
### 2.3 改进config_check.sh脚本
**文件**`lib/shell/config_check.sh`
**位置**`output_json` 函数
**改进内容**
- 确保PLATFORM变量始终有值
- 为config_count和total_ips设置默认值
- 添加默认值处理
---
## 3. 代码修改详情
### 3.1 ConvertFrom-ShellJson 函数
**修改内容**
1. 参数改为非必需,设置默认空字符串
2. 添加空输入检查
3. 提前返回null并输出错误日志
**代码变更**
```powershell
function ConvertFrom-ShellJson {
param(
[Parameter(Mandatory=$false)] [string]$JsonString = ""
)
# 检查输入是否为空
if ([string]::IsNullOrEmpty($JsonString)) {
Write-Log -Level "ERROR" -Message "[SHELL] Shell脚本未返回任何输出"
return $null
}
# ... 继续JSON解析
}
```
### 3.2 Invoke-RemoteShellCheck 函数
**修改内容**
添加更详细的错误日志和空输出检查
**代码变更**
```powershell
# 检查执行结果
if (-not $result) {
Write-Log -Level "ERROR" -Message "[SHELL] SSH命令执行失败: $ScriptName"
Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $RemotePath 2>/dev/null" | Out-Null
return $null
}
# 检查输出
if (-not $result.Output) {
Write-Log -Level "WARN" -Message "[SHELL] 脚本未返回输出: $ScriptName (退出码: $($result.ExitCode))"
}
# ... 返回结果
if ($result.Output) {
return $result.Output -join "`n"
} else {
return $null
}
```
### 3.3 config_check.sh output_json 函数
**修改内容**
确保变量有默认值
**代码变更**
```bash
output_json() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 确保PLATFORM已设置
if [ -z "$PLATFORM" ]; then
PLATFORM="unknown"
fi
echo "{"
json_kv "check_type" "config_ip_check"
json_kv "timestamp" "$timestamp"
json_kv "platform" "$PLATFORM" false
# ... 检测逻辑
# 确保返回默认值
config_count=${config_count:-0}
total_ips=${total_ips:-0}
# ... 继续输出
}
```
---
## 4. 测试验证
### 4.1 测试场景
| 测试项 | 测试条件 | 预期结果 |
|--------|----------|----------|
| 正常输出 | 配置文件存在 | 成功返回JSON |
| 空输出 | 无配置文件 | 返回默认JSON而不是null |
| 参数验证 | 传入空字符串 | 显示错误日志,不崩溃 |
### 4.2 验证步骤
1. 运行 `.\check_server_health.ps1`
2. 选择Shell模式
3. 输入服务器连接信息
4. 观察配置IP检测输出
5. 确认无参数验证错误
---
## 5. 部署说明
### 5.1 文件同步
- 主脚本:`check_server_health.ps1`
- Shell脚本:`lib/shell/config_check.sh`
- 目标位置:`C:\Users\UBAINS\Desktop\Test-module\`
### 5.2 依赖要求
- plink.exe(SSH连接工具)
- pscp.exe(文件传输工具)
---
## 6. 优化功能回填
| 序号 | 优化内容 | 状态 |
|------|----------|------|
| 1 | 修复ConvertFrom-ShellJson参数验证 | ✅ 已完成 |
| 2 | 添加空输入检查 | ✅ 已完成 |
| 3 | 改进Invoke-RemoteShellCheck错误处理 | ✅ 已完成 |
| 4 | config_check.sh添加默认值处理 | ✅ 已完成 |
---
*文档结束*
# _PRD_模块化拆分后NTP检测脚本执行错误
> 来源:
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health.ps1`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/common.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/ntp_check.sh`
## 1. 问题报错信息
### 1.1 问题一:NTP检测报错
```
[2026-05-13 14:59:59] [INFO] ========== 开始NTP检测 (Shell模式) ==========
[2026-05-13 14:59:59] [INFO] 已找到 pscp: C:\Users\UBAINS\Desktop\Test-module\pscp.exe
[2026-05-13 15:00:00] [SUCCESS] 文件上传成功: C:\Users\UBAINS\Desktop\Test-module\lib\shell\common.sh -> root@192.168.5.46:/tmp/health_check/
[2026-05-13 15:00:00] [INFO] 已找到 pscp: C:\Users\UBAINS\Desktop\Test-module\pscp.exe
[2026-05-13 15:00:00] [SUCCESS] 文件上传成功: C:\Users\UBAINS\Desktop\Test-module\lib\shell\ntp_check.sh -> root@192.168.5.46:/tmp/health_check/
[2026-05-13 15:00:01] [ERROR] [SHELL] JSON解析失败: 传入的对象无效,应为“:”或“}”。 (186): {
"check_type": "ntp_check",
"timestamp": "2026-05-13 15:00:00",
"ntp_service": "chronyd",
"service_status": "running",
"time_sync_status": "synchronized",
"ntp_servers": ""ntp1.aliyun.com,ntp2.aliyun.com,ntp3.aliyun.com"",
"time_offset": "N/A",
}
Write-Log : 无法对参数“Level”执行参数验证。参数“DEBUG”不属于 ValidateSet 属性指定的集合“INFO,WARN,ERROR,SUCCESS”。请提供一个此集合中的参数,然后重试此命令。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:538 字符: 26
+ Write-Log -Level "DEBUG" -Message "[SHELL] 原始输出: $JsonString"
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Write-Log],ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Write-Log
[2026-05-13 15:00:01] [ERROR] [NTP] Shell脚本执行失败
```
### 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
*文档结束*
# _PRD_模块化拆分后NTP检测脚本执行错误_问题处理 - 计划执行文档
> 创建时间:2026-05-13
> 状态:已完成
> 来源:`Docs/PRD/服务自检/问题修复/_PRD_模块化拆分后NTP检测脚本执行错误_问题处理.md`
---
## 1. 问题分析
### 1.1 核心问题
NTP检测Shell脚本输出的JSON格式错误,导致PowerShell解析失败。
### 1.2 错误详情
#### 问题:JSON格式错误 - 双引号嵌套
```
[SHELL] JSON解析失败: 传入的对象无效,应为":"或"}"。 (186): {
"check_type": "ntp_check",
"timestamp": "2026-05-13 15:00:00",
"ntp_service": "chronyd",
"service_status": "running",
"time_sync_status": "synchronized",
"ntp_servers": ""ntp1.aliyun.com,ntp2.aliyun.com,ntp3.aliyun.com"",
"time_offset": "N/A",
}
```
**原因分析**
- `ntp_servers` 字段值包含双层引号:`""ntp1.aliyun.com...""`
- Shell脚本中手动添加了引号,而 `json_kv` 函数也会添加引号
- 导致JSON格式错误
**问题代码**
```bash
json_kv "ntp_servers" "\"$ntp_servers\""
```
---
## 2. 解决方案
### 2.1 修复ntp_check.sh中的引号嵌套
**文件**`lib/shell/ntp_check.sh`
**位置**`output_json` 函数
**修改前**
```bash
json_kv "ntp_servers" "\"$ntp_servers\""
```
**修改后**
```bash
json_kv "ntp_servers" "$ntp_servers"
```
**说明**
- `json_kv` 函数已经会自动为值添加引号
- 不需要手动在调用时添加引号
- 移除多余的 `\` 和 `"` 即可
### 2.2 json_kv函数说明
**函数定义**(common.sh):
```bash
json_kv() {
local key="$1"
local value="$2"
local is_last="${3:-false}"
if [ "$is_last" = "true" ]; then
echo " \"$key\": \"$value\""
else
echo " \"$key\": \"$value\","
fi
}
```
**正确用法**:
```bash
# 字符串值 - json_kv会自动添加引号
json_kv "name" "value"
# 带逗号分隔的字符串 - 直接传值即可
json_kv "servers" "server1,server2,server3"
# 数字值 - 也被当作字符串处理
json_kv "port" "8080"
```
---
## 3. 代码修改详情
### 3.1 ntp_check.sh output_json 函数
**修改内容**:
移除 `ntp_servers` 参数中多余的引号
**代码变更**:
```bash
# 修改前
echo "{"
json_kv "check_type" "ntp_check"
json_kv "timestamp" "$timestamp"
json_kv "ntp_service" "$ntp_daemon"
json_kv "service_status" "$ntp_status" false
json_kv "time_sync_status" "$time_sync"
json_kv "ntp_servers" "\"$ntp_servers\"" # 错误:双层引号
json_kv "time_offset" "$time_offset" false
echo "}"
# 修改后
echo "{"
json_kv "check_type" "ntp_check"
json_kv "timestamp" "$timestamp"
json_kv "ntp_service" "$ntp_daemon"
json_kv "service_status" "$ntp_status" false
json_kv "time_sync_status" "$time_sync"
json_kv "ntp_servers" "$ntp_servers" # 正确:单层引号
json_kv "time_offset" "$time_offset" false
echo "}"
```
---
## 4. 输出对比
### 4.1 修改前(错误)
```json
{
"check_type": "ntp_check",
"timestamp": "2026-05-13 15:00:00",
"ntp_service": "chronyd",
"service_status": "running",
"time_sync_status": "synchronized",
"ntp_servers": ""ntp1.aliyun.com,ntp2.aliyun.com,ntp3.aliyun.com"",
"time_offset": "N/A"
}
```
### 4.2 修改后(正确)
```json
{
"check_type": "ntp_check",
"timestamp": "2026-05-13 15:00:00",
"ntp_service": "chronyd",
"service_status": "running",
"time_sync_status": "synchronized",
"ntp_servers": "ntp1.aliyun.com,ntp2.aliyun.com,ntp3.aliyun.com",
"time_offset": "N/A"
}
```
---
## 5. 测试验证
### 5.1 测试场景
| 测试项 | 测试条件 | 预期结果 |
|--------|----------|----------|
| JSON格式 | 执行ntp_check.sh | 无双层引号 |
| JSON解析 | PowerShell解析JSON | 成功解析 |
| 值正确性 | 检查ntp_servers值 | 保持原始字符串 |
### 5.2 验证步骤
1. 运行 `.\check_server_health.ps1`
2. 选择Shell模式
3. 输入服务器连接信息
4. 观察NTP检测输出
5. 确认JSON格式正确
6. 确认ntp_servers值正确
---
## 6. 部署说明
### 6.1 文件同步
- Shell脚本:`lib/shell/ntp_check.sh`
- 目标位置:`C:\Users\UBAINS\Desktop\Test-module\lib\shell\`
### 6.2 依赖要求
- plink.exe(SSH连接工具)
- pscp.exe(文件传输工具)
---
## 7. 优化功能回填
| 序号 | 优化内容 | 状态 |
|------|----------|------|
| 1 | 修复ntp_servers双层引号问题 | ✅ 已完成 |
| 2 | 验证JSON输出格式正确 | ✅ 已完成 |
| 3 | 确保ntp_servers值保持不变 | ✅ 已完成 |
---
## 8. 注意事项
### 8.1 json_kv使用规范
在使用 `json_kv` 函数时:
- **不需要**手动为值添加引号
- **不需要**使用转义字符 `\"`
- 直接传递变量或字符串即可
### 8.2 其他Shell脚本检查
此问题可能存在于其他使用 `json_kv` 的脚本中,建议检查:
- `dns_check.sh`
- `middleware_check.sh`
- `resource_check.sh`
- `java_check.sh`
- `docker_check.sh`
- `config_check.sh`
**检查要点**:搜索是否有 `\"` 这样的转义引号用法
---
*文档结束*
# _PRD_模块化拆分后脚本执行错误
> 来源:
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/java_check.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health.ps1`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/dns_check.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/resource_check.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/middleware_check.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/config_check.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/ntp_check.sh`
## 1. 问题报错信息
### 1.1 问题一:java服务检测报错
```
[2026-05-13 14:40:18] [INFO] ========== 检测 ujava 服务 (容器: ujava2) (Shell模式) ==========
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:427 字符: 69
+ ... emote -Server $Server -LocalPath $localCommonPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:432 字符: 69
+ ... emote -Server $Server -LocalPath $localScriptPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
[2026-05-13 14:40:20] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
[2026-05-13 14:40:20] [ERROR] [UJAVA] Shell脚本执行失败
```
### 1.2 问题二:DNS检测报错
```ignorelang
[2026-05-13 14:40:21] [INFO] ========== 开始DNS检测 (Shell模式) ==========
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:427 字符: 69
+ ... emote -Server $Server -LocalPath $localCommonPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:432 字符: 69
+ ... emote -Server $Server -LocalPath $localScriptPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
[2026-05-13 14:40:22] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
[2026-05-13 14:40:22] [ERROR] [DNS] Shell脚本执行失败
```
### 1.3 问题三:资源检测报错
```ignorelang
[2026-05-13 14:40:22] [INFO] ========== 开始资源检测 (Shell模式) ==========
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:427 字符: 69
+ ... emote -Server $Server -LocalPath $localCommonPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:432 字符: 69
+ ... emote -Server $Server -LocalPath $localScriptPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
[2026-05-13 14:40:23] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
[2026-05-13 14:40:23] [ERROR] [资源] Shell脚本执行失败
```
### 1.4 问题四:中间件检测报错
```ignorelang
[2026-05-13 14:41:20] [INFO] ========== 开始中间件检测 (Shell模式) ==========
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:427 字符: 69
+ ... emote -Server $Server -LocalPath $localCommonPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:432 字符: 69
+ ... emote -Server $Server -LocalPath $localScriptPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
[2026-05-13 14:41:21] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
[2026-05-13 14:41:21] [ERROR] [中间件] Shell脚本执行失败
```
### 1.5 问题五:配置IP检测报错
```ignorelang
[2026-05-13 14:41:22] [INFO] ========== 开始配置IP检测 (Shell模式) ==========
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:427 字符: 69
+ ... emote -Server $Server -LocalPath $localCommonPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:432 字符: 69
+ ... emote -Server $Server -LocalPath $localScriptPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
[2026-05-13 14:41:23] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
[2026-05-13 14:41:23] [ERROR] [配置] Shell脚本执行失败
```
### 1.6 问题六:NTP检测报错
```ignorelang
[2026-05-13 14:41:55] [INFO] ========== 开始NTP检测 (Shell模式) ==========
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:427 字符: 69
+ ... emote -Server $Server -LocalPath $localCommonPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
Copy-File-To-Remote : 找不到与参数名称“RemotePath”匹配的参数。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:432 字符: 69
+ ... emote -Server $Server -LocalPath $localScriptPath -RemotePath $remote ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-File-To-Remote],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Copy-File-To-Remote
[2026-05-13 14:41:56] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
[2026-05-13 14:41:56] [ERROR] [NTP] Shell脚本执行失败
```
### 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
*文档结束*
# _PRD_模块化拆分后脚本执行错误_问题处理 - 计划执行文档
> 创建时间:2026-05-13
> 状态:已完成
> 来源:`Docs/PRD/服务自检/问题修复/_PRD_模块化拆分后脚本执行错误_问题处理.md`
---
## 1. 问题分析
### 1.1 核心问题
Shell模式执行时出现两个主要错误:
1. **参数错误**`Copy-File-To-Remote` 函数调用参数顺序和名称不匹配
2. **JSON解析失败**:SSH命令输出包含非JSON内容(chmod命令等)
### 1.2 错误详情
#### 问题一:Copy-File-To-Remote参数错误
```
Copy-File-To-Remote : 找不到与参数名称"RemotePath"匹配的参数。
```
**原因**
- `Copy-File-To-Remote` 函数定义:`(LocalPath, Server, RemoteDir)`
- 错误调用方式:`-Server $Server -LocalPath xxx -RemotePath xxx`
#### 问题二:JSON解析失败
```
[SHELL] JSON解析失败: 无效的 JSON 基元: chmod。
```
**原因**
- SSH命令输出包含chmod等shell命令的输出
- `ConvertFrom-ShellJson` 函数未正确过滤非JSON内容
---
## 2. 解决方案
### 2.1 修复Copy-File-To-Remote调用
**文件**`check_server_health.ps1`
**位置**`Upload-ShellScript` 函数
**修改前**
```powershell
Copy-File-To-Remote -Server $Server -LocalPath $localCommonPath -RemotePath $remoteCommonPath
```
**修改后**
```powershell
Copy-File-To-Remote -LocalPath $localCommonPath -Server $Server -RemoteDir $RemotePath
```
### 2.2 改进JSON解析函数
**文件**`check_server_health.ps1`
**位置**`ConvertFrom-ShellJson` 函数
**改进内容**
- 添加大括号计数逻辑,提取完整JSON块
- 过滤掉chmod、mkdir等命令输出
- 过滤掉plink/pscp的错误信息
- 添加调试日志输出原始内容
### 2.3 改进SSH命令执行
**文件**`check_server_health.ps1`
**位置**`Invoke-RemoteShellCheck``Upload-ShellScript` 函数
**改进内容**
- 所有shell命令添加 `2>/dev/null` 抑制错误输出
- `Upload-ShellScript` 返回布尔值表示成功/失败
- 添加本地文件存在性检查
---
## 3. 代码修改详情
### 3.1 Upload-ShellScript 函数
**修改内容**
1. 添加本地文件存在性检查
2. 修正 `Copy-File-To-Remote` 调用参数
3. 返回布尔值而非路径字符串
4. 添加详细错误日志
**代码变更**
```powershell
# 检查本地脚本是否存在
$localCommonPath = Join-Path $SCRIPT_DIR "lib\shell\common.sh"
$localScriptPath = Join-Path $SCRIPT_DIR "lib\shell\$ScriptName"
if (-not (Test-Path $localCommonPath)) {
Write-Log -Level "ERROR" -Message "[SHELL] 本地脚本不存在: $localCommonPath"
return $false
}
# 修正参数顺序
Copy-File-To-Remote -LocalPath $localCommonPath -Server $Server -RemoteDir $RemotePath
```
### 3.2 Invoke-RemoteShellCheck 函数
**修改内容**
1. 检查上传成功状态
2. 执行命令添加 `2>/dev/null` 抑制错误输出
3. 改进返回值处理
**代码变更**
```powershell
$uploadSuccess = Upload-ShellScript -Server $Server -ScriptName $ScriptName -RemotePath $RemotePath
if (-not $uploadSuccess) {
Write-Log -Level "ERROR" -Message "[SHELL] 脚本上传失败: $ScriptName"
return $null
}
$cmd = "cd $RemotePath 2>/dev/null && chmod +x common.sh $ScriptName 2>/dev/null && ./$ScriptName $Arguments 2>/dev/null"
```
### 3.3 ConvertFrom-ShellJson 函数
**修改内容**
1. 实现大括号计数逻辑提取完整JSON
2. 过滤常见命令输出
3. 添加调试日志
**代码变更**
```powershell
$lines = $JsonString -split "`n"
$jsonLines = @()
$inJson = $false
$braceCount = 0
foreach ($line in $lines) {
$trimmed = $line.Trim()
# 跳过空行和常见错误信息
if ([string]::IsNullOrEmpty($trimmed)) { continue }
if ($trimmed -match "^(chmod|mkdir|rm|cd|\$|pscp:|plink:|Fatal|Network|Connection)") { continue }
# 找到JSON开始位置
if (-not $inJson -and $trimmed.StartsWith("{")) {
$inJson = $true
}
if ($inJson) {
$jsonLines += $line
$braceCount += ($line.ToCharArray() | Where-Object { $_ -eq "{" } | Measure-Object).Count
$braceCount -= ($line.ToCharArray() | Where-Object { $_ -eq "}" } | Measure-Object).Count
if ($braceCount -le 0 -and $trimmed.EndsWith("}")) {
break
}
}
}
```
---
## 4. 测试验证
### 4.1 测试场景
| 测试项 | 测试命令 | 预期结果 |
|--------|----------|----------|
| Java服务检测 | 选择Shell模式 | 成功检测ujava服务状态 |
| DNS解析检测 | 选择Shell模式 | 成功测试域名解析 |
| 资源检测 | 选择Shell模式 | 成功获取CPU/内存/磁盘数据 |
| 中间件检测 | 选择Shell模式 | 成功检测Redis/MySQL/EMQX/FastDFS |
| 配置IP检测 | 选择Shell模式 | 成功扫描配置文件IP |
| NTP检测 | 选择Shell模式 | 成功获取NTP状态 |
### 4.2 验证步骤
1. 运行 `.\check_server_health.ps1`
2. 选择Shell模式
3. 输入服务器连接信息
4. 观察各检测项输出
5. 确认无参数错误
6. 确认JSON解析成功
---
## 5. 部署说明
### 5.1 文件同步
- 源文件:`E:\GithubData\ubains-module-test\AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.ps1`
- 目标位置:`C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1`
### 5.2 依赖要求
- plink.exe(SSH连接工具)
- pscp.exe(文件传输工具)
- lib/shell/*.sh(Shell脚本文件)
---
## 6. 优化功能回填
| 序号 | 优化内容 | 状态 |
|------|----------|------|
| 1 | 修正Copy-File-To-Remote函数参数顺序 | ✅ 已完成 |
| 2 | 改进JSON解析函数,过滤非JSON输出 | ✅ 已完成 |
| 3 | 添加2>/dev/null抑制命令错误输出 | ✅ 已完成 |
| 4 | 添加文件存在性检查 | ✅ 已完成 |
| 5 | 改进错误日志输出 | ✅ 已完成 |
---
*文档结束*
# _PRD_模块化拆分后资源检测脚本执行错误
> 来源:
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health.ps1`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/common.sh`
- `AuxiliaryTool/ScriptTool/ServiceSelfInspection/lib/shell/resource_check.sh`
## 1. 问题报错信息
### 1.1 问题一:资源检测报错
```
[2026-05-13 14:58:21] [INFO] ========== 开始资源检测 (Shell模式) ==========
[2026-05-13 14:58:22] [INFO] 已找到 pscp: C:\Users\UBAINS\Desktop\Test-module\pscp.exe
[2026-05-13 14:58:22] [SUCCESS] 文件上传成功: C:\Users\UBAINS\Desktop\Test-module\lib\shell\common.sh -> root@192.168.5.46:/tmp/health_check/
[2026-05-13 14:58:22] [INFO] 已找到 pscp: C:\Users\UBAINS\Desktop\Test-module\pscp.exe
[2026-05-13 14:58:23] [SUCCESS] 文件上传成功: C:\Users\UBAINS\Desktop\Test-module\lib\shell\resource_check.sh -> root@192.168.5.46:/tmp/health_check/
[2026-05-13 14:58:24] [ERROR] [SHELL] JSON解析失败: 无效的 JSON 基元: 已用。
Write-Log : 无法对参数“Level”执行参数验证。参数“DEBUG”不属于 ValidateSet 属性指定的集合“INFO,WARN,ERROR,SUCCESS”。请提供一个此集合中的参数,然后重试此命令。
所在位置 C:\Users\UBAINS\Desktop\Test-module\check_server_health.ps1:538 字符: 26
+ Write-Log -Level "DEBUG" -Message "[SHELL] 原始输出: $JsonString"
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Write-Log],ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Write-Log
[2026-05-13 14:58:24] [ERROR] [资源] Shell脚本执行失败
```
### 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
*文档结束*
# _PRD_模块化拆分后资源检测脚本执行错误_问题处理 - 计划执行文档
> 创建时间:2026-05-13
> 状态:已完成
> 来源:`Docs/PRD/服务自检/问题修复/_PRD_模块化拆分后资源检测脚本执行错误_问题处理.md`
---
## 1. 问题分析
### 1.1 核心问题
资源检测Shell脚本执行时出现两个错误:
1. **JSON解析失败**:输出包含中文"已用"等非JSON内容
2. **Write-Log参数错误**:使用了不存在的"DEBUG"日志级别
### 1.2 错误详情
#### 问题一:Write-Log参数错误
```
Write-Log : 无法对参数"Level"执行参数验证。参数"DEBUG"不属于 ValidateSet 属性指定的集合"INFO,WARN,ERROR,SUCCESS"。
```
**原因**
- `Write-Log` 函数仅支持:INFO、WARN、ERROR、SUCCESS 四种级别
- 代码中使用了不存在的 `DEBUG` 级别
#### 问题二:JSON解析失败
```
[SHELL] JSON解析失败: 无效的 JSON 基元: 已用。
```
**原因**
- Shell脚本输出包含stderr错误信息
- JSON解析函数未完全过滤非JSON内容
- 中文输出干扰JSON解析
---
## 2. 解决方案
### 2.1 修复Write-Log日志级别
**文件**`check_server_health.ps1`
**位置**`ConvertFrom-ShellJson` 函数
**修改前**
```powershell
Write-Log -Level "DEBUG" -Message "[SHELL] 原始输出: $JsonString"
```
**修改后**
```powershell
Write-Log -Level "INFO" -Message "[SHELL] 原始输出前200字符: $($JsonString.Substring(0, [Math]::Min(200, $JsonString.Length)))"
```
### 2.2 改进JSON解析函数
**文件**`check_server_health.ps1`
**位置**`ConvertFrom-ShellJson` 函数
**改进内容**
- 添加中文输出过滤(已用、总用量、Mem:、Swap:、Cpu等)
- 限制调试输出长度,避免控制台溢出
### 2.3 Shell脚本添加stderr抑制
**文件**`lib/shell/*.sh`(所有检测脚本)
**位置**:主函数调用前
**添加内容**
```bash
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main
```
---
## 3. 代码修改详情
### 3.1 ConvertFrom-ShellJson 函数
**修改内容**
1. 添加中文命令输出过滤
2. 修改DEBUG日志级别为INFO
3. 限制调试输出长度
**代码变更**
```powershell
# 跳过常见命令输出和错误信息(包括中文输出)
if ($trimmed -match "^(chmod|mkdir|rm|cd|\$|pscp:|plink:|Fatal|Network|Connection|已用|总用量|Mem:|Swap:|Cpu)") { continue }
# ... JSON解析逻辑 ...
catch {
Write-Log -Level "ERROR" -Message "[SHELL] JSON解析失败: $($_.Exception.Message)"
Write-Log -Level "INFO" -Message "[SHELL] 原始输出前200字符: $($JsonString.Substring(0, [Math]::Min(200, $JsonString.Length)))"
return $null
}
```
### 3.2 所有Shell脚本
**修改内容**
在主函数调用前添加 `exec 2>/dev/null`
**受影响文件**
- `lib/shell/resource_check.sh`
- `lib/shell/java_check.sh`
- `lib/shell/docker_check.sh`
- `lib/shell/middleware_check.sh`
- `lib/shell/dns_check.sh`
- `lib/shell/ntp_check.sh`
- `lib/shell/config_check.sh`
**代码变更**
```bash
# 执行主函数
# 将所有stderr重定向到/dev/null,确保只输出JSON
exec 2>/dev/null
main
```
---
## 4. 测试验证
### 4.1 测试场景
| 测试项 | 测试命令 | 预期结果 |
|--------|----------|----------|
| 资源检测 | 选择Shell模式 | 成功获取CPU/内存/磁盘数据 |
| JSON解析 | 查看日志输出 | 无"已用"等中文干扰 |
| 日志级别 | 查看调试输出 | 使用INFO级别正常输出 |
### 4.2 验证步骤
1. 运行 `.\check_server_health.ps1`
2. 选择Shell模式
3. 输入服务器连接信息
4. 观察资源检测输出
5. 确认JSON解析成功
6. 确认日志输出正常
---
## 5. 部署说明
### 5.1 文件同步
- 主脚本:`check_server_health.ps1`
- Shell脚本库:`lib/shell/*.sh`
- 目标位置:`C:\Users\UBAINS\Desktop\Test-module\`
### 5.2 依赖要求
- plink.exe(SSH连接工具)
- pscp.exe(文件传输工具)
---
## 6. 优化功能回填
| 序号 | 优化内容 | 状态 |
|------|----------|------|
| 1 | 修正Write-Log日志级别错误 | ✅ 已完成 |
| 2 | 添加中文输出过滤 | ✅ 已完成 |
| 3 | 限制调试输出长度 | ✅ 已完成 |
| 4 | Shell脚本添加stderr抑制 | ✅ 已完成 |
| 5 | 更新所有7个Shell脚本 | ✅ 已完成 |
---
*文档结束*
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论