#xml #powershell #foreach
#xml #powershell #foreach
Вопрос:
Ниже приведен мой XML
<step>
<filename>tk1872_01datanx1872nx1872_base_include_wrn.dat</filename>
<lookfor>W:\ugs</lookfor>
<replace>C:BMugs</replace>
</step>
<step>
<filename>NX1872_01toolscheckReg.ps1</filename>
<lookfor>W:\ugs</lookfor>
<replace>C:BMugs</replace>
</step>
<step>
<filename>tk1872_01datanx1872nx1872_base_include_wrn.dat</filename>
<lookfor>Y:\CATIAcfgV5</lookfor>
<replace>C:BMCATIAcfgV5</replace>
</step>
Я пытаюсь ссылаться на приведенный выше XML для замены строк в файлах, которые упоминаются в filename node .
Например: я взял ссылку из XML для имени файла и его пути, например (tk1872_01 data nx1872 nx1872_base_include_wrn.dat), затем я ищу «W:ugs « и заменить на «C:BMugs «. Это первый шаг для цикла foreach, но если вы видите 3-й шаг с тем же именем файла, но я ищу другую строку, но цикл foreach не выполнил замену для второго вхождения с тем же именем файла.
Ниже приведен мой код, который я использую в Powershell
$testXML = C:testsettings.xml
[xml]$SpecialXMLFile = Get-Content $testXML
foreach ($step in $SpecialXMLFile.step)
{
$_aux = C:temp
$_aux_dest = D:temp
$srcfilename = $_aux "" $step.filename
$destfilename = $_aux_dest "" $step.filename
$strun = Get-Content $srcfilename
$strun -replace $step.lookfor, $step.change | Out-File -Encoding ascii $destfilename
}
Не могли бы вы сообщить мне, где я делаю неправильно, или мне нужно добавить еще несколько шагов?
Комментарии:
1. $SpecialXMLFile.configuration.replace.step должен быть $SpecialXMLFile.configuration.step.replace нет?
2. Ваш опубликованный XML-файл не соответствует пути, указанному вами в вашем коде. Отсутствует [конфигурация], и в вашем коде есть элементы, которые не разъясняют, для чего они существуют или существуют, наконец, вы действительно переоцениваете этот вариант использования. XML / JSON является родным для PowerShell и имеет командлеты для работы с ними.
3. @Esperento57 да, извините, у меня есть еще два узла перед шаговыми узлами, о которых я забыл упомянуть
4. @postanote Я использую XML-файл, потому что я не хочу менять код каждый раз, когда я просто вношу изменения в XML, чтобы изменить свой код, и, соответственно, он будет вносить изменения в файлы конфигурации.
5. Итак, вы хотите сказать, что никогда не знаете, что находится в файле XML? Вы можете просто настроить свой код на чтение получаемого вами XML и запрашивать, с каким узлом / значением вы хотите работать. В приведенном мной примере он просто захватывает основной узел конфигурации и отображает узел шага для работы. Затем вы используете эти параметры, чтобы делать то, что хотите. Вы можете запросить что-либо из этого. Никаких изменений в базовом коде не требуется.
Ответ №1:
Продолжая мой комментарий.
Get-Command -Name '*xml*'
# Results
<#
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet ConvertTo-Xml 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Export-Clixml 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Import-Clixml 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Select-Xml 3.1.0.0 Microsoft.PowerShell.Utility
#>
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Select-Xml).Parameters
(Get-Command -Name Select-Xml).Parameters.Keys
Get-help -Name Select-Xml -Examples
Get-help -Name Select-Xml -Full
Get-help -Name Select-Xml -Online
Таким образом, почему бы не использовать собственный командлет Select-Xml, чтобы легко выбрать нужный узел и изменить его по мере необходимости.
Пример получения значений шага:
(@'
<configuration>
<step>
<filename>tk1872_01datanx1872_base_include_wrn.dat</filename>
<lookfor>W:\ugs</lookfor>
<replace>C:BMugs</replace>
</step>
<step>
<filename>NX1872_01toolscheckReg.ps1</filename>
<lookfor>W:\ugs</lookfor>
<replace>C:BMugs</replace>
</step>
<step>
<filename>tk1872_01datanx1872nx1872_base_include_wrn.dat</filename>
<lookfor>Y:\CATIAcfgV5</lookfor>
<replace>C:BMCATIAcfgV5</replace>
</step>
</configuration>
'@ |
Select-Xml -XPath '//configuration').Node.step
# Results
<#
filename lookfor replace
-------- ------- -------
tk1872_01datanx1872_base_include_wrn.dat W:\ugs C:BMugs
NX1872_01toolscheckReg.ps1 W:\ugs C:BMugs
tk1872_01datanx1872nx1872_base_include_wrn.dat Y:\CATIAcfgV5 C:BMCATIAcfgV5
#>
Затем замените на что угодно, используя то, что указано.
Комментарии:
1. Не беспокойтесь, это случается. Это просто заставляет нас предполагать / догадываться о том, что происходит на самом деле, хотя это усложняет реагирование. Например, первые две строки в вашем коде являются избыточными, если только вы не запрашиваете это имя файла. $_aux * нигде не определен в вашем коде, поэтому то, что в нем содержится, остается загадкой, а $step.change не имеет ссылки в XML. Итак, покажите больше вашего XML, определения кода и ожидаемого результата.
2. @postnote как я уже сказал, я сожалею, что предоставил короткую информацию. Я снова отредактировал свой вопрос с правильной информацией, как вы просили. Я думаю, что мне не удалось заставить вас понять, что я хотел. моя настоящая проблема заключается в том, что когда я выполняю цикл через шаг в Powershell в выходном файле, он изменяет только первое вхождение, которое
<lookfor>W:\ugs</lookfor><replace>C:BMugs</replace></step>
не является другим, которое<lookfor>Y:\CATIAcfgV5</lookfor><replace>C:BMCATIAcfgV5</replace>
3. Я хочу сказать, что если имя файла встречается два раза, то оно считается только первым вхождением, а не вторым. это не замена строки во второй раз, когда там есть то же имя файла.
Ответ №2:
Спасибо за дальнейшие разъяснения.
XML, который вы показываете, пропускает корневой элемент, поэтому я использовал это:
[xml]$xml = @"
<root>
<step>
<filename>tk1872_01datanx1872nx1872_base_include_wrn.dat</filename>
<lookfor>W:\ugs</lookfor>
<replace>C:BMugs</replace>
</step>
<step>
<filename>NX1872_01toolscheckReg.ps1</filename>
<lookfor>W:\ugs</lookfor>
<replace>C:BMugs</replace>
</step>
<step>
<filename>tk1872_01datanx1872nx1872_base_include_wrn.dat</filename>
<lookfor>Y:\CATIAcfgV5</lookfor>
<replace>C:BMCATIAcfgV5</replace>
</step>
</root>
"@
В реальной жизни вы бы хотели прочитать это из файла с помощью
[xml]$xml = Get-Content -Path 'C:testsettings.xml' -Raw
Дело в том, что на разных этапах один и тот же файл необходимо настроить, и в вашем коде этого не происходит, потому что файл считывается дважды, чтобы заменить только одно lookfor
значение. Во второй раз считывается тот же (неизмененный) исходный файл, и снова выполняется только одна замена.
Что вам нужно сделать, так это сгруппировать шаги, в которых имя файла совпадает, получить содержимое из этого файла только один раз и выполнить все необходимые замены. Затем выводите в новый файл по пути назначения.
$sourcePath = 'C:temp'
$destination = 'D:temp'
# combine the steps where the filenames are equal
# if your xml has a different named root, change that here
$xml.root.step | Group-Object filename | ForEach-Object {
# create the paths from the groups name
$fileIn = Join-Path -Path $sourcePath -ChildPath $_.Name
$fileOut = Join-Path -Path $destination -ChildPath $_.Name
# test if the output directory path for the file exists. If not create it first
$pathOut = [System.IO.Path]::GetDirectoryName($fileOut)
if (!(Test-Path -Path $pathOut -PathType Container)) {
$null = New-Item -Path $pathOut -ItemType Directory
}
# read the input file as a single multiline string
$content = Get-Content -Path $fileIn -Raw
foreach ($item in $_.Group) {
# replace as many times as needed
$content = $content -replace $item.lookfor, $item.replace
}
# are you sure about the -Encoding ascii ??
$content | Out-File -FilePath $fileOut -Encoding ascii
}