我有一个存档文件(类似于.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
变量的作用吗?
答案 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($_) }
}
它应该可以满足您的期望。