我编辑XML文件,并使用PowerShell在记事本中打开它们并替换文本字符串。给定两个不同的分隔符(开始和结束),它们在XML文件中出现多次,我想完全删除分隔符之间的文本(是否同时删除分隔符也没关系给我)。
在下面的示例文本中,我想完全删除起始定界符和结束定界符之间的文本,但保留之前和之后的所有文本。
我面临的问题是每行文本的末尾都有换行符,这使我无法执行简单操作:
-replace "<!--A6-->.*?<!--A6 end-->", "KEVIN"
开始分隔符:
<!--A6-->
停止定界符:
<!--A6 end-->
示例文字:
<listItem>
<para>Apple iPhone 6</para>
</listItem>
<listItem>
<para>Apple iPhone 8</para>
</listItem>
<!--A6-->
<listItem>
<para>Apple iPhone X</para>
</listItem>
<!--A6 end-->
</randomList></para>
</levelledPara>
<levelledPara>
<!--A6-->
<title>Available Apple iPhone Colors</title>
<para>The current iPhone model is available in
the follow colors. You can purchase this model
in store, or online.</para>
<!--A6 end-->
<para>If the color option that you want is out
of stock, you can find them at the following
website link.</para>
当前代码:
$Directory = "C:\Users\hellokevin\Desktop\PSTest"
$FindBook = "Book"
$ReplaceBook = "Novel"
$FindBike = "Bike"
$ReplaceBike = "Bicycle"
Get-ChildItem -Path $Directory -Recurse |
Select-Object -Expand FullName|
ForEach-Object {
(Get-Content $_) -replace $FindBook,$ReplaceBook -replace "<!--A6-->.*?<!--A6 end-->", "KEVIN" |
Set-Content ($_ + "_new.xml")
}
任何帮助将不胜感激。作为PowerShell的新手,我不知道如何在代码的每一行末尾添加换行符。感谢您的光临!
答案 0 :(得分:1)
极端是不建议在XML文件上使用搜索和替换的,因此应不惜一切代价避免,因为这样很容易损坏XML。
有更好的修改XML的方法,它们都遵循以下模式:
对于您的情况(“删除标记之间的节点”),可能如下:
以下程序将完全做到这一点(并同时删除“ A6”注释本身):
$doc = New-Object xml
$doc.Load("C:\path\to\your.xml")
$toRemove = @()
$A6flag = $false
foreach ($node in $doc.SelectNodes('//node()')) {
if ($node.NodeType -eq "Comment") {
if ($node.Value -eq 'A6') {
$A6flag = $true
$toRemove += $node
} elseif ($node.Value -eq 'A6 end') {
$A6flag = $false
$toRemove += $node
}
} elseif ($A6flag) {
$toRemove += $node
}
}
foreach ($node in $toRemove) {
[void]$node.ParentNode.RemoveChild($node)
}
$doc.Save("C:\path\to\your_modified.xml")
您也可以在foreach
循环内进行字符串替换:
if ($node.NodeType -eq "Text") {
$node.Value = $node.Value -replace "Apple","APPLE"
}
在单个-replace
上执行$node.Value
是安全的。不在整个XML上执行-replace
。
答案 1 :(得分:0)
注意:
通常,为了进行稳健的处理,应使用专用XML解析器 来解析XML文本。
在特定情况下,使用 regex 是便捷快捷方式 ,但需要注意的是,仅因为删除的线段是自包含元素或元素序列 而起作用;如果此假设不成立,则修改将使XML文档无效。
此外,可能存在字符编码问题,因为将XML文件读取为文本并不符合显式要求。文件的XML声明中可能存在encoding
属性-有关详细信息,请参见底部。
也就是说,以下技术适用于修改没有特定形式结构的纯文本文件。
您需要使用s
(SingleLine
)regex option来确保.
也匹配换行符-这样的选项,如果是内联使用,则必须放在正则表达式开始处的(?...)
内;即'(?s)...'
。
[\s\S]
代替.
;此表达式匹配任何为空白char的字符。或非空格字符,因此匹配任何字符,包括换行符。要完全删除感兴趣的行,您还必须匹配前面和后面的换行符。
(Get-Content -Raw file.xml) -replace '(?s)\r?\n<!--A6-->.*?<!--A6 end-->\r?\n'
Get-Content -Raw file.xml
将文件作为一个整体读入内存 (单个字符串)。
Get-Content
在没有BOM的情况下对文件的字符编码进行了假设:Windows PowerShell假定为ANSI编码,而PowerShell [Core] v6 +现在明智地假定为UTF-8。由于Get-Content
是读取cmdlet的通用文本文件,因此 不会意识到XML输入文件的XML声明中潜在的encoding
属性(例如,<?xml version="1.0" encoding="ISO-8859-1"?>
Set-Content
在Windows PowerShell和无BOM的UTF-8 PowerShell [Core] v6 +中默认为ANSI。 -Encoding
和Get-Content
的{{1}}参数 Set-Content
匹配Windows风格的CRLF换行符和Unix风格的仅LF换行符。
如果不能保证换行符在感兴趣的行之前/之后,请使用\r?\n
而不是(?:\r?\n)?
。
要验证结果字符串仍然是有效的XML文档,只需将命令(或其捕获的结果)强制转换为\r?\n
:[xml]
如果发现文档已损坏,请使用Tomalak's fully robust, but more complex XML-parsing answer。
如果您使用[xml] ((Get-Content ...) -replace ...)
来读取XML文件作为文本,并且该文件既没有UTF-8 BOM也没有UTF-16 / UTF-32 BOM,{{1 }}进行假设:在Windows PowerShell中采用ANSI编码(例如Windows-1252),在PowerShell [Core] v6 +中更合理地采用UTF-8编码。由于 Get-Content
是读取通用文本文件的cmdlet,因此不不知道XML输入文件的XML声明中潜在的Get-Content
属性强>。
如果您知道实际的编码,请使用Get-Content
参数进行指定。
使用具有相同值的encoding
,以后再用-Encoding
保存文件:通常在PowerShell中,一旦文件读取cmdlet将数据加载到内存中, 不会保留有关其原始编码的信息 ,后来使用文件写入cmdlet(例如-Encoding
)会使用其固定的默认编码,该编码也是ANSI Windows PowerShell和PowerShell [Core] v6 +中的无BOM的UTF-8。请注意,不幸的是,不同的cmdlet在Windows PowerShell中具有不同的默认值,而PowerShell [Core] v6 +应该一致地默认为UTF-8。
System.Xml.XmlDocument
.NET类型(其PowerShell类型加速器为Set-Content
)提供强大的XML解析,并使用其Set-Content
和[xml]
方法提供更好的编码支持 if 如果文档的XML声明包含显式的.Load()
属性,则为使用的编码命名:
如果存在这样的属性(例如.Save()
),则encoding
和<?xml version="1.0" encoding="ISO-8859-1"?>
都将使用它。
.Load()
属性的输入文件,将被正确读取,并以相同的编码保存。.Save()
属性中命名的编码反映的是输入文件的实际编码。否则,如果文件中没有BOM ,则,假定假定为 (无BOM)UTF-8,与PowerShell [Core] v6 +的{{1 }} / encoding
-这很明智,因为根据{{3},既没有encoding
属性也没有UTF-8或UTF-16 BOM的XML文档应默认为UTF-8。 };如果文件确实具有BOM表,则只允许使用UTF-8和UTF-16,而无需在Get-Content
属性中命名编码,尽管实际上{ {1}}还可以正确读取带有BOM的UTF-32文件。
这意味着Set-Content
将不保留(带有BOM的)UTF-16或UTF-32文件的编码。 encoding
属性,并将创建一个无BOM的UTF-8文件。
如果您要检测文件的实际编码-根据文件的BOM /缺少文件的编码或encoding
属性(如果存在)来推断,请通过XmlDocument
实例:
.Save()
如果给定文件是不兼容,并且您知道实际使用的编码和/或您想保存给定文件(请确保它与encoding
属性并不矛盾(如果有),您可以显式指定编码(相当于将encoding
与XmlTextReader
/ # Create an XML reader.
$xmlReader = [System.Xml.XmlTextReader]::new(
"$pwd/some.xml" # IMPORTANT: use a FULL PATH
)
# Read past the declaration, which detects the encoding,
# whether via the presence / absence of a BOM or an explicit
# `encoding` attribute.
$null = $xmlReader.MoveToContent()
# Report the detected encoding.
$xmlReader.Encoding
# You can now pass the reader to .Load(), if needed
# See next section for how to *save* with the detected encoding.
$xmlDoc = [xml]::new()
$xmlDoc.Load($xmlReader)
$xmlReader.Close()
一起使用),使用encoding
/ -Encoding
方法重载通过使用给定编码构造的Get-Content
/ Set-Content
实例接受.Load()
实例;例如:
.Save()
将文件路径传递给.NET方法的一般警告:始终使用完整路径 ,因为.NET对当前目录的理解通常不同于PowerShell的思想。