[英]PowerShell - Removing multiple lines of text between delimiters in a text file
我編輯 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 的新手,我不知道如何在代碼中的每一行末尾考慮換行符。 感謝您的關注!
在 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"
}
在單個$node.Value
上執行-replace
是安全的。 對整個 XML 執行-replace
不是。
筆記:
通常,為了進行穩健的處理,您應該使用專用的 XML 解析器來解析 XML 文本。
在手頭的特定情況下,使用正則表達式是一種方便的快捷方式,但需要注意的是,它僅適用於被刪除的行塊是自包含元素或元素序列; 如果此假設不成立,則修改將使 XML 文檔無效。
此外,可能存在字符編碼問題,因為將 XML 文件作為文本讀取並不會遵守文件的 XML 聲明中可能存在的顯式encoding
屬性 - 有關詳細信息,請參閱底部部分。
也就是說,下面的技術適用於修改沒有特定正式結構的純文本文件。
您需要使用s
( SingleLine
) regex 選項來確保.
也匹配換行符- 如果使用內聯,此類選項必須放在正則表達式開頭的(?...)
內; 也就是說, '(?s)...'
在這種情況下。
[\\s\\S]
而不是.
,正如x15所建議的那樣; 此表達式匹配任何作為空白字符的字符。 或非空白字符,因此匹配任何字符,包括換行符。要完全刪除感興趣的行,您還必須匹配前面和后面的換行符。
(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 中默認為 ANSI,在無 BOM 的 UTF-8 PowerShell [Core] v6+ 中默認為 ANSI。Get-Content
和Set-Content
使用-Encoding
參數 \\r?\\n
匹配 Windows 風格的 CRLF 換行符和 Unix 風格的 LF-only 換行符。
使用(?:\\r?\\n)?
而不是\\r?\\n
如果換行符不能保證在感興趣的行之前/之后。
要驗證結果字符串是否仍然是有效的 XML 文檔,只需將命令(或其捕獲的結果) [xml]
為[xml]
: [xml] ((Get-Content ...) -replace ...)
如果您發現文檔已損壞,請使用Tomalak 的完全健壯但更復雜的 XML 解析答案。
如果您使用Get-Content
將 XML 文件讀取為 text ,並且該文件既沒有 UTF-8 BOM 也沒有 UTF-16 / UTF-32 BOM,則Get-Content
做出一個假設:它假設 ANSI 編碼(例如,Windows -1252) 在 Windows PowerShell 中,更明智的是,在 PowerShell [Core] v6+ 中使用 UTF-8 編碼。 由於Get-Content
是通用的文本文件讀取 cmdlet,因此它不知道 XML 輸入文件的 XML 聲明中的潛在encoding
屬性。
如果您知道實際編碼,請使用-Encoding
參數來指定它。
使用具有相同值的-Encoding
稍后使用Set-Content
保存文件:與 PowerShell 中的通常情況一樣,一旦數據被文件讀取 cmdlet 加載到內存中,就不會保留有關其原始編碼的信息,並且使用諸如Set-Content
類的文件寫入 cmdlet 稍后使用其固定的默認編碼,在 Windows PowerShell 中再次使用 ANSI,在 PowerShell [Core] v6+ 中使用無 BOM 的 UTF-8。 請注意,不幸的是,不同的 cmdlet 在 Windows PowerShell 中具有不同的默認值,而 PowerShell [Core] v6+ 值得稱贊的是始終默認為 UTF-8。
System.Xml.XmlDocument
.NET 類型(其 PowerShell 類型加速器是[xml]
)提供強大的 XML 解析,如果文檔的 XML 聲明包含顯式encoding
,則使用其.Load()
和.Save()
方法提供更好的編碼支持命名使用的編碼的屬性:
如果存在這樣的屬性(例如, <?xml version="1.0" encoding="ISO-8859-1"?>
), .Load()
和.Save()
都會遵守它。
encoding
屬性的輸入文件將被正確讀取,並以相同的編碼保存。encoding
屬性中命名的encoding
反映了輸入文件的實際編碼。 否則,如果文件沒有 BOM ,則假定(BOM-less) UTF-8,就像 PowerShell [Core] v6+ 的Get-Content
/ Set-Content
- 這是明智的,因為 XML 文檔既沒有encoding
屬性也沒有根據W3C XML 建議,UTF-8 或 UTF-16 BOM 應默認為 UTF-8; 如果文件確實有 BOM,則只允許使用 UTF-8 和 UTF-16,而無需在encoding
屬性中命名encoding
,盡管實際上XmlDocument
也可以正確讀取帶有 BOM 的 UTF-32 文件。
這意味着.Save()
不會保留沒有encoding
屬性的(帶 BOM 的)UTF-16 或 UTF-32 文件的encoding
,而是創建一個無 BOM 的 UTF-8 文件。
如果您想檢測文件的實際編碼 - 從其 BOM 推斷/不存在,或者,如果存在, 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()
方法重載接受Stream
實例,通過使用給定編碼構造的StreamReader
/ StreamWriter
實例; 例如:
# Get the encoding to use, matching the input file's. # Eg, if the input file is ISO-8859-1-encoded, but lacks # an `encoding` attribute in the XML declaration. $enc = [System.Text.Encoding]::GetEncoding('ISO-8859-1') # Create a System.Xml.XmlDocument instance. $xmlDoc = [xml]::new() # Create a stream reader for the input XML file # with explicit encoding. $streamIn = [System.IO.StreamReader]::new( "$pwd/some.xml", # IMPORTANT: use a FULL PATH $enc ) # Read and parse the file. $xmlDoc.Load($streamIn) # Close the stream $streamIn.Close() # ... process the XML DOM. # Create a stream *writer* for saving back to the file # with the same encoding. $streamOut = [System.IO.StreamWriter]::new( "$pwd/t.xml", # IMPORTANT: use a FULL PATH $false, # don't append $enc # same encoding as above in this case. ) # Save the XML DOM to the file. $xmlDoc.Save($streamOut) # Close the stream $streamOut.Close()
將文件路徑傳遞給 .NET 方法的一般警告:始終使用完整路徑,因為 .NET 對當前目錄的理解通常與 PowerShell 不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.