[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory = $true)] [string]$Path, [switch]$Recurse ) <# 用法示例 1. 批量修复当前目录下所有 XML 路径文件 powershell -ExecutionPolicy Bypass -File "C:\Users\Tellme\apps\NavisworksTransport-rail-mount-modes\scripts\Fix-RailPreferredNormals.ps1" -Path "C:\Users\Tellme\Documents\NavisworksTransport\模型" 2. 递归修复目录及其子目录中的所有 XML 路径文件 powershell -ExecutionPolicy Bypass -File "C:\Users\Tellme\apps\NavisworksTransport-rail-mount-modes\scripts\Fix-RailPreferredNormals.ps1" -Path "C:\Users\Tellme\Documents\NavisworksTransport\模型" -Recurse 3. 只修复单个 XML 路径文件 powershell -ExecutionPolicy Bypass -File "C:\Users\Tellme\apps\NavisworksTransport-rail-mount-modes\scripts\Fix-RailPreferredNormals.ps1" -Path "C:\Users\Tellme\Documents\NavisworksTransport\模型\副本_人工_0331_133243.xml" 脚本规则 - 只处理 pathType="Rail" 且 railMountMode="OverRail" 的路径 - 只修复 railPreferredNormalY < 0 的路径 - 修复方式为将 railPreferredNormalX / Y / Z 三个分量一起取反 - 原文件会被直接覆盖,同时自动保留一个同名 .bak 备份文件 #> Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Format-NormalValue { param( [Parameter(Mandatory = $true)] [double]$Value ) return $Value.ToString("0.###", [System.Globalization.CultureInfo]::InvariantCulture) } function Test-ParseableDoubleText { param( [Parameter(Mandatory = $true)] [string]$Text, [ref]$Value ) return [double]::TryParse( $Text, [System.Globalization.NumberStyles]::Float -bor [System.Globalization.NumberStyles]::AllowLeadingSign, [System.Globalization.CultureInfo]::InvariantCulture, $Value) } function Update-RouteNode { param( [Parameter(Mandatory = $true)] [System.Xml.XmlElement]$RouteNode, [Parameter(Mandatory = $true)] [string]$SourcePath ) if ($RouteNode.GetAttribute("pathType") -ne "Rail") { return $false } if ($RouteNode.GetAttribute("railMountMode") -ne "OverRail") { return $false } $xText = $RouteNode.GetAttribute("railPreferredNormalX") $yText = $RouteNode.GetAttribute("railPreferredNormalY") $zText = $RouteNode.GetAttribute("railPreferredNormalZ") if ([string]::IsNullOrWhiteSpace($xText) -or [string]::IsNullOrWhiteSpace($yText) -or [string]::IsNullOrWhiteSpace($zText)) { return $false } $x = 0.0 $y = 0.0 $z = 0.0 if (-not (Test-ParseableDoubleText -Text $xText -Value ([ref]$x)) -or -not (Test-ParseableDoubleText -Text $yText -Value ([ref]$y)) -or -not (Test-ParseableDoubleText -Text $zText -Value ([ref]$z))) { throw "文件 '$SourcePath' 中存在无法解析的 railPreferredNormal 值。" } if ($y -ge 0) { return $false } $RouteNode.SetAttribute("railPreferredNormalX", (Format-NormalValue -Value (-$x))) $RouteNode.SetAttribute("railPreferredNormalY", (Format-NormalValue -Value (-$y))) $RouteNode.SetAttribute("railPreferredNormalZ", (Format-NormalValue -Value (-$z))) return $true } function Save-XmlDocument { param( [Parameter(Mandatory = $true)] [xml]$Document, [Parameter(Mandatory = $true)] [string]$TargetPath ) $settings = New-Object System.Xml.XmlWriterSettings $settings.Indent = $true $settings.IndentChars = " " $settings.Encoding = New-Object System.Text.UTF8Encoding($false) $settings.NewLineChars = "`r`n" $settings.NewLineHandling = [System.Xml.NewLineHandling]::Replace $writer = [System.Xml.XmlWriter]::Create($TargetPath, $settings) try { $Document.Save($writer) } finally { $writer.Dispose() } } function Update-XmlFile { param( [Parameter(Mandatory = $true)] [string]$FilePath ) [xml]$xml = Get-Content -LiteralPath $FilePath -Raw $routeNodes = @($xml.SelectNodes("//*[local-name()='Route']")) if ($null -eq $routeNodes -or $routeNodes.Count -eq 0) { return [pscustomobject]@{ FilePath = $FilePath Modified = $false RouteCount = 0 } } $modifiedRouteCount = 0 foreach ($routeNode in $routeNodes) { if (Update-RouteNode -RouteNode $routeNode -SourcePath $FilePath) { $modifiedRouteCount++ } } if ($modifiedRouteCount -gt 0) { $backupPath = "$FilePath.bak" if ($PSCmdlet.ShouldProcess($FilePath, "修复 railPreferredNormal 并写回 XML")) { if (-not (Test-Path -LiteralPath $backupPath)) { Copy-Item -LiteralPath $FilePath -Destination $backupPath } Save-XmlDocument -Document $xml -TargetPath $FilePath } } return [pscustomobject]@{ FilePath = $FilePath Modified = ($modifiedRouteCount -gt 0) RouteCount = $modifiedRouteCount } } $resolvedPath = Resolve-Path -LiteralPath $Path $item = Get-Item -LiteralPath $resolvedPath $files = @() if ($item.PSIsContainer) { $childParams = @{ LiteralPath = $item.FullName File = $true } if ($Recurse) { $childParams["Recurse"] = $true } $files = @(Get-ChildItem @childParams | Where-Object { $_.Extension -ieq ".xml" }) } else { $files = @($item) } if ($files.Count -eq 0) { Write-Host "未找到可处理的 XML 文件。" exit 0 } $results = @() foreach ($file in $files) { $results += Update-XmlFile -FilePath $file.FullName } $results = @($results) $modifiedFiles = @($results | Where-Object { $_.Modified }) $modifiedRoutes = 0 if ($modifiedFiles.Count -gt 0) { $measure = $modifiedFiles | Measure-Object -Property RouteCount -Sum if ($null -ne $measure -and $null -ne $measure.Sum) { $modifiedRoutes = [int]$measure.Sum } } Write-Host ("扫描文件: {0}" -f $results.Count) Write-Host ("修改文件: {0}" -f $modifiedFiles.Count) Write-Host ("修复路径: {0}" -f $modifiedRoutes) foreach ($result in $modifiedFiles) { Write-Host ("已修复: {0} (路径数={1})" -f $result.FilePath, $result.RouteCount) }