【问题标题】:Foreach loop in Powershell via XML通过 XML 在 Powershell 中的 Foreach 循环
【发布时间】:2021-03-29 11:11:29
【问题描述】:

下面是我的 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 来替换 filename 节点中提到的文件中的字符串。

例如:我从 XML 中获取文件名及其路径的参考,例如 (tk1872_01\data\nx1872\nx1872_base_include_wrn.dat) 然后我查找 "W:\ugs" 并替换为 “C:\BM\ugs”。这是 foreach 循环的第一步,但如果您看到具有相同文件名的第三步,但我正在寻找不同的字符串,但 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 否?
  • 您发布的 XML 与您在代码中声明的路径不匹配。它缺少[配置],并且您的代码中有一些项目没有说明有什么或有什么用途,最后,您确实过度设计了这个用例。 XML/JSON 是 PowerShell 原生的,它有 cmdlet 可以使用它们。
  • @Esperento57 是的,抱歉,我忘记提及的步骤节点之前还有两个节点
  • @postanote 我正在使用 XML 文件,因为我不想每次都更改代码,每次我只是在 XML 中进行更改以修改我的代码,因此它会在配置文件中进行更改。
  • 那么,您是说您永远不知道 XML 文件中的内容吗?您只需将代码设置为读取您收到的 XML 并提示您要使用的节点/值。在我提供的示例中,它只是抓取主配置节点并显示要使用的步骤节点。然后,您可以根据需要使用这些参数。你可以提示任何一个。无需更改基本代码。

标签: xml powershell foreach


【解决方案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 cmdlet 来轻松选择您想要的节点并根据需要进行修改。

获取步长值的示例:

(@'
<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、代码定义和预期结果。
  • @postnote 正如我所说我很抱歉我提供了简短的信息。我已按照您的要求使用正确的信息再次编辑了我的问题。我想我没能让你明白我想要什么。我真正担心的是,当我在输出文件中遍历 Powershell 中的一个步骤时,它只会更改第一个出现的 &lt;lookfor&gt;W:\\ugs&lt;/lookfor&gt;&lt;replace&gt;C:\BM\ugs&lt;/replace&gt;&lt;/step&gt; 而不是另一个 &lt;lookfor&gt;Y:\\CATIAcfgV5&lt;/lookfor&gt;&lt;replace&gt;C:\BM\CATIAcfgV5&lt;/replace&gt;
  • 我的意思是如果文件名出现两次,它只被认为是第一次出现,而不是第二次出现。当存在相同的文件名时,它不会第二次替换字符串。
【解决方案2】:

感谢您进一步解释。

您显示的 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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    • 1970-01-01
    • 2013-08-19
    • 2013-07-09
    相关资源
    最近更新 更多