在存档文件中,更改所有XML文件以删除包含特定@Name值的节点

时间:2019-06-17 20:49:01

标签: xml powershell

我有一个存档文件(类似于.zip或.7z文件),其中包含许多使用常规结构的XML文件:

<DataRequest>
  <DataObject>
    <DataProperty Name= "prop1">
      <SimpleProperty Value="abc"
    </DataProperty>
    <DataProperty Name= "propB">
      <SimpleProperty Value="123"
    </DataProperty>
    <DataProperty Name= "propX">
      <SimpleProperty Value="xyz"
    </DataProperty>
  </DataObject>
</DataRequest>

我想批量修改这些文件以删除所有<DataProperty>参数为{prop1“的@Name节点。

编辑 按照下面的@AnsgarWiechers答案,我已经更新了我的工作代码

$zipFileName = "C:\Users\me\filename.bxp"
Add-Type -Assembly  System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::Open($zipfileName, "Update")
$editFile = $zip.Entries.Where({$_.Name -Like "*.xml"})
$xml = New-Object Xml

$editFile | ForEach-Object {
    $xml.Load($_.FullName)
    $xml.SelectNodes('//DataProperty[@Name="prop1"]') |
        ForEach-Object { $_.ParentNode.RemoveChild($_) }
    $xml.Save('C:\path\to\output.xml')
}

这里的问题是文件的保存方式。我不想输出新的.xml文件。相反,我想更改.xml文件在.bxp存档中的位置。

当前该过程是手动的。在文件资源管理器窗口中,我可以右键单击.bxp文件,然后选择“ 7zip-> Open Archive”,这将打开一个记事本编辑器。从该编辑器中保存的任何更改都将影响.bxp中的文件。我希望以编程方式复制该过程。

如果这不可能,我可以输出.xml文件并将其手动添加到存档中。在这种情况下,我的问题是更改循环中每个.xml文件的输出文件名。我可以设置一个将继承文件名并输出相同文件名的变量吗?这就是$_.FullName变量的作用吗?

1 个答案:

答案 0 :(得分:1)

您的语法无效。 pipe将一个语句/命令的输出连接到另一个语句/命令的输入。您可以在管道符号后的 后面加长行,因为PowerShell将随后识别出该语句在下一行继续。但是,如果在管道符号之前换行,PowerShell将无法自动检测到该行的延续并分别解释这两行,因为第一行包含完整的有效语句,因此导致您观察到的语法错误。

如果要在管道之前换行,必须 在第一行的末尾使用反引号(`)换行。

$xml.SelectNodes('//DataProperty') `
    | ForEach-Object { $_.ParentNode.RemoveChild($_) }

但是同样,在管道符号的之后换行是更好的样式:

$xml.SelectNodes('//DataProperty') |
    ForEach-Object { $_.ParentNode.RemoveChild($_) }

此外,没有理由在$xml = [xml](Get-Content $_.FullName)$xml.SelectNodes('//DataProperty')之间放置管道。第一条语句将已解析的XML数据结构分配给一个变量,然后在第二条语句中使用该变量。第二条语句不读取管道输入。从技术上讲,您可以将已解析的XML数据输入到管道中,但这将需要在ForEach-Object周围添加另一个SelectNodes()

也可以提出这样的说法

$xml = New-Object Xml
$xml.Load($_.FullName)

相比,

是一种更干净的XML数据加载方式。

$xml = [xml](Get-Content $_.FullName)

但是在大​​多数情况下,两种方法都可以使用。

在排除所有语法问题的情况下,XPath语言中的过滤器是方括号中的表达式。在您的情况下,您要删除属性为Name且值为prop1的节点,因此过滤器如下所示:[@Name="prop1"]

将您的代码更改为以下内容:

$xml = New-Object Xml
$editFile | ForEach-Object {
    $xml.Load($_.FullName)
    $xml.SelectNodes('//DataProperty[@Name="prop1"]') |
        ForEach-Object { $_.ParentNode.RemoveChild($_) }
}

它应该可以满足您的期望。