Как вызвать сценарий PowerShell из командной строки, который обновляет XML-файл?
Вызовите сценарий PowerShell из командной строки, чтобы обновить узлы файла XML.
Пример XML-файла
<Job>
<ProcFile name="jobName" />
<Category name="Indicative">
<Item name="rbfVersion">versionname</Item>
<Item name="rbfRelease">releaseNumber</Item>
</Category>
<Category name="Parameters">
<Item name="MmsPzapAppId">Value1</Item>
</Category>
</Job>
Сценарий PowerShell подготовлен для обновления узлов XML-файла.
param ($workingDir, $fileName, $arrayOfObject)
if (!$workingDir) {
throw "Working Directory Parameter is missing"
}elseif(!$fileName){
throw "File name or Runbook name Parameter is missing"
}elseif(!$arrayOfObject){
throw "Input parameters are missing"
}
$keyVauleHash = $arrayOfObject
#concatenating two parameters to get full path
$filePath = "$workingDir\$fileName"
#Reading Values
Write-Host "Working Directory: $workingDir"
Write-Host "File Name : $fileName"
Write-Host "Full Path : $filePath"
Write-Host "Number of Nodes to be updated:"$keyVauleHash.Count
#Get Content of XML file and store it in $xml file
[xml]$xml = Get-Content $filePath
#Looping through each key:value pair from array of object and updating respective nodes
foreach($item in $keyVauleHash.GetEnumerator()){
$key = $item.key
$value = $item.value
$parametersNode = $xml.Job.Category | where {$_.Name -eq 'Parameters'}
$foundNode = $parametersNode.Item | where {$_.Name -eq $key}
Write-Output $foundNode
$foundNode.'#text' = $value
Write-Output $foundNode
}
#Saving the changes
$xml.Save($filePath)
Использование сценария или вызов команды
pwsh -command C:\Path\To\PowerShellScript\UpdateXML.ps1 -workingDir 'C:\Path\To\xmlFile' -fileName 'xmlFileName.xml' -arrayOfObject '@{InputFile="Value"}'
Вышеприведенный сценарий следует вызывать из командной строки.
Пробовал также другой метод, пробовал PowerShell.exe вместо pwsh, тоже не помогло.
альтернативная команда вызова
PowerShell.exe C:\Path\To\PowerShellScript\UpdateXML.ps1 -workingDir 'C:\Path\To\xmlFile' -fileName 'xmlFileName.xml' -arrayOfObject '@{InputFile="Value"}'
Оба метода вызова на самом деле не работают. Я получаю эту ошибку ниже.
The property '#text' cannot be found on this object. Verify that the property exists and can be set.
2 ответа
Ваша непосредственная проблема связана с синтаксисом ваших вызовов CLI (powershell.exe
для Windows PowerShell,pwsh
для PowerShell (Core) 7+)):
-arrayOfObject '@{InputFile="Value"}'
из-за использования вложения'...'
, передает строку с дословным содержимым @{InputFile=Value}
к вашему сценарию-arrayOfObject
параметр, а не объект хеш-таблицы , как вы предполагали, - также обратите внимание на потерю неэкранированного"
символы, которые удаляются при первоначальном разборе командной строки.
Вместо этого используйте одно из следующих действий:
# Note the need to \-escape the " chars.
-arrayOfObject @{InputFile=\"Value\"}
# Alternative, with single quotes
-arrayOfObject @{InputFile='Value'}
Существуют также проблемы с вашим кодом PowerShell , в частности, с отсутствием защиты от отсутствия целевого интересующего XML-элемента.
То есть, если$foundNode
бывает$null
,$foundNode.'#text' = ...
выдает ошибку, которую вы видели (попробуйте$null.foo = 'bar'
, например).
Ваш код PowerShell можно как упростить, так и сделать более надежным — см. следующий раздел.
Надежная и идиоматическая переформулировка вашего скрипта, основанная на PowerShell:
# Make the parameters mandatory and specify *data types* for them.
param (
[Parameter(Mandatory)]
[string] $workingDir,
[Parameter(Mandatory)]
[string] $fileName,
[Parameter(Mandatory)]
[hashtable] $arrayOfObject # Probably worth renaming this.
)
# Load the XML file into an in-memory DOM
$filePath = Convert-Path -LiteralPath "$workingDir\$fileName"
# Note: Constructing an [xml] instance and then using .Load() is
# more robust than using [xml]$xml = Get-Content $filePath
$xml = [xml]::new(); $xml.Load($filePath)
# Determine the parent element of the elements to modify.
$parametersNode = $xml.Job.Category | Where-Object Name -eq Parameters
# Loop over all key-value pairs passed in and modify
# child elements whose 'Name' attribute matches a key with the
# corresponding value.
foreach ($key in $arrayOfObject.Keys) {
if ($foundNode = $parametersNode.Item | Where-Object Name -eq $key) {
$foundNode.'#text' = $arrayOfObject[$key]
}
}
# Save the modified document.
$xml.Save($filePath)
Следующий код использует XML и словарь для изменения значений узлов.
using assembly System.Xml.Linq
$inputFilename = "c:\temp\test.xml"
$outputFilename = "c:\temp\test1.xml"
$doc = [System.Xml.Linq.XDocument]::Load($inputFilename)
$items = $doc.Descendants("Item")
$dict = [System.Collections.Generic.Dictionary[string, [System.Xml.Linq.XElement]]]::new()
$items | foreach { $name = $_.Attribute("name").Value; $element = $_; $dict.Add($name, $element) }
$newValues = @{
rbfVersion = 'version1'
rbfRelease = '2.0'
MmsPzapAppId = '123'
}
foreach($newValue in $newValues.Keys)
{
$key = $newValue
$value = $newValues[$newValue]
$element = $dict[$key]
$element.SetValue($value)
}
$doc.Save($outputFilename)