Цикл Foreach в Powershell через XML

Ниже мой XML

<step>
    <filename>tk1872_01\data\nx1872\nx1872_base_include_wrn.dat</filename>
    <lookfor>W:\\ugs</lookfor>
    <replace>C:\BM\ugs</replace>
</step>
<step>
    <filename>NX1872_01\tools\checkReg.ps1</filename>
    <lookfor>W:\\ugs</lookfor>
    <replace>C:\BM\ugs</replace>
</step>
<step>
    <filename>tk1872_01\data\nx1872\nx1872_base_include_wrn.dat</filename>
    <lookfor>Y:\\CATIAcfgV5</lookfor>
    <replace>C:\BM\CATIAcfgV5</replace>
</step>

Я пытаюсь сослаться выше XML, чтобы заменить строки в файлах, которые упомянуты в узле имени файла.

Например: я взял ссылку из XML для имени файла и его пути, например (tk1872_01\data\nx1872\nx1872_base_include_wrn.dat), затем я ищу «W:\ugs» и заменяю на «C:\BM\ugs». Это первый шаг для цикла foreach, но если вы видите 3-й шаг с тем же именем файла, но я ищу другую строку, но цикл foreach не выполнял замену второго вхождения с тем же именем файла.

Ниже приведен мой код, который я использую в Powershell.

$testXML = C:\test\settings.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
}

Не могли бы вы сообщить мне, где я делаю неправильно, или мне нужно добавить еще несколько шагов?

$SpecialXMLFile.configuration.replace.step должен быть $SpecialXMLFile.configuration.step.replace нет?

Esperento57 18.12.2020 21:02

Ваш опубликованный XML не соответствует пути, который вы указали в своем коде. Отсутствует [конфигурация], и в вашем коде есть элементы, которые не поясняют, что они есть или для чего они нужны. Наконец, вы действительно слишком усложняете этот вариант использования. XML/JSON являются родными для PowerShell, и у него есть командлеты для работы с ними.

postanote 19.12.2020 09:35

@ Esperento57 Esperento57 да, извините, у меня есть еще два узла перед ступенчатыми узлами, о которых я забыл упомянуть.

rohan jannu 19.12.2020 09:55

@postanote Я использую файл XML, потому что я не хочу менять код каждый раз и каждый раз, когда я просто вношу изменения в XML, чтобы изменить свой код, и, соответственно, он будет вносить изменения в файлы конфигурации.

rohan jannu 19.12.2020 09:57

Итак, вы говорите, что никогда не знаете, что находится в файле XML? Вы можете просто настроить свой код для чтения XML, который вы получаете, и запрашивать, с каким узлом/значением вы хотите работать. В примере, который я предоставил, он просто захватывает основной узел конфигурации и отображает узел шага, с которым нужно работать. Затем вы используете эти параметры, чтобы делать то, что хотите. Вы можете подсказать что-либо из этого. Никаких изменений в базовом коде не требуется.

postanote 19.12.2020 10:56

Пожалуйста, для ясности, отредактируйте свой вопрос и покажите нам желаемый результат. Мне очень неясно, что вы хотите заменить в файле.

Theo 19.12.2020 15:51

@ Тео, я отредактировал свой вопрос, пожалуйста, просмотрите его и дайте мне знать, если вам нужны дополнительные разъяснения по этому поводу.

rohan jannu 19.12.2020 21:03
Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
7
383
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В продолжение моего комментария.

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_01\data\nx1872_base_include_wrn.dat</filename>
        <lookfor>W:\\ugs</lookfor>
        <replace>C:\BM\ugs</replace>
    </step>
    <step>
        <filename>NX1872_01\tools\checkReg.ps1</filename>
        <lookfor>W:\\ugs</lookfor>
        <replace>C:\BM\ugs</replace>
    </step>
    <step>
        <filename>tk1872_01\data\nx1872\nx1872_base_include_wrn.dat</filename>
        <lookfor>Y:\\CATIAcfgV5</lookfor>
        <replace>C:\BM\CATIAcfgV5</replace>
    </step>
</configuration>
'@  | 
Select-Xml -XPath '//configuration').Node.step
# Results
<#
filename                                          lookfor        replace         
--------                                          -------        -------         
tk1872_01\data\nx1872_base_include_wrn.dat        W:\\ugs        C:\BM\ugs       
NX1872_01\tools\checkReg.ps1                      W:\\ugs        C:\BM\ugs       
tk1872_01\data\nx1872\nx1872_base_include_wrn.dat Y:\\CATIAcfgV5 C:\BM\CATIAcfgV5
#>

Затем замените чем угодно, используя то, что дано.

Не беспокойтесь, это случается. Это просто заставляет нас предполагать/догадываться о том, что происходит на самом деле, хотя это затрудняет ответ. Например, первые две строки в вашем коде избыточны, если только вы не запрашиваете это имя файла. $_aux* не определен нигде в вашем коде, поэтому то, что в нем находится, остается загадкой, а $step.change не имеет ссылки в XML. Итак, покажите больше своего XML, определения кода и ожидаемого результата.

postanote 19.12.2020 11:00

@postnote, как я уже сказал, мне жаль, что я предоставил краткую информацию. Я снова отредактировал свой вопрос, указав правильную информацию, как вы просили. Я думаю, что мне не удалось заставить вас понять, что я хотел. моя реальная проблема заключается в том, что когда я выполняю шаг в Powershell в выходном файле, он изменяет только первое вхождение, которое является <lookfor>W:\\ugs</lookfor><replace>C:\BM\ugs</replace></step‌​>, а не другим, которое <lookfor>Y:\\CATIAcfgV5</lookfor><replace>C:\BM\CATIAcfgV5</‌​replace>

rohan jannu 19.12.2020 13:42

Я имею в виду, что если имя файла встречается два раза, оно считается только первым, а не вторым. он не заменяет строку во второй раз, когда присутствует одно и то же имя файла.

rohan jannu 19.12.2020 14:05

Спасибо за дальнейшее объяснение.

XML, который вы показываете, пропускает корневой элемент, поэтому я использовал это:

[xml]$xml = @"
<root>
    <step>
        <filename>tk1872_01\data\nx1872\nx1872_base_include_wrn.dat</filename>
        <lookfor>W:\\ugs</lookfor>
        <replace>C:\BM\ugs</replace>
    </step>
    <step>
        <filename>NX1872_01\tools\checkReg.ps1</filename>
        <lookfor>W:\\ugs</lookfor>
        <replace>C:\BM\ugs</replace>
    </step>
    <step>
        <filename>tk1872_01\data\nx1872\nx1872_base_include_wrn.dat</filename>
        <lookfor>Y:\\CATIAcfgV5</lookfor>
        <replace>C:\BM\CATIAcfgV5</replace>
    </step>
</root>
"@

В реальной жизни вы хотели бы прочитать это из файла, используя

[xml]$xml = Get-Content -Path 'C:\test\settings.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
}

Другие вопросы по теме