如何找出“替换”是否实际上做了什么

时间:2019-02-28 10:42:07

标签: regex powershell

我对PowerShell相当熟悉,并且我正在使用Get-Content传递到字符串替换表达式中。但是,我只想在实际更改的情况下将“新”内容写回到文件中。

我一直在使用简单的新/旧文件内容比较,但是在较大的文件上,它相当慢(例如,非常慢)。在我看来,正则表达式替换实际上运行非常快,因此,如果替换完成后有某种方式询问PowerShell,是否找到了任何东西,那将是理想的选择。如果未进行任何更改,只需不要将文件写回。

我尝试测试$Matches.count,但我得到了

  

由于尚未设置变量'$ Matches',因此无法检索。

我想念什么吗?我真的不想在进行替换之前先寻找匹配项,因为这似乎也很浪费(而且违反了告诉-不要询问的要求)。

这是我目前的操作方式:

function Convert-ToUTF8 {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string[]] $File)
    process {
        $File |
            %{
                $fileFullName = $_

                Write-Verbose "Loading $filefullname..."
                $content = (Get-Content $fileFullName)

                Write-Verbose "   Fixing xml prolog..."
                $newcontent = $content -replace '^<\?xml version="(\d+\.\d+)" encoding="(.+)"\?>$', '<?xml version="$1" encoding="UTF-8"?>'

                Write-Verbose "   Checking to see if there were changes..."
                $changed = $newcontent -ne $content

                if ($changed) {
                    if ($PSCmdlet.ShouldProcess("Write changes to $filefullname")) {
                        Write-Host "Writing changes to $filefullname..."
                        $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
                        [System.IO.File]::WriteAllLines($fileFullName, $newcontent, $Utf8NoBomEncoding)
                    }
                } else {
                    Write-Host "No changes to $filefullname."
                }
            }
    }
}

2 个答案:

答案 0 :(得分:1)

我们已经指出,通常最好使用适当的XML解析,但是您已经澄清了,为了保持以后的分散注意力,您保留了输入文件的准确格式。


实际上,判断-replace操作是否实际执行替换的唯一方法是将输入字符串与结果字符串进行比较。

(正如Mathias R. Jessen所指出的那样,只有-match运算符(和switch -regex)会填充自动的$Matches变量,以反映正则表达式匹配操作的结果)。 / p>

在最简单的情况下:

$original = 'foo'
$potentiallyModified = $original -replace 'x', 'y'

$replacementWasMade = $original -cne $potentiallyModified

注意:

  • -cne(而不是-ne)用于区分大小写 ,还可以检测替换仅改变输入字符串大小写的情况

  • 可以想象,已经执行了有效的无操作替换(例如'foo' -replace 'o', 'o'),而上述操作并未检测到该替换;就是说,虽然在这种情况下会返回一个新的[string]实例,但这通常并不重要,因为通常使用 value 相等性而不是 reference 平等-见下文。

如果在这种情况下性能至关重要-我怀疑在大多数情况下这很重要-您可以利用以下{strong { 3}})的事实,如果指定的正则表达式与不匹配不匹配,则按原样返回输入字符串 (相同的[string]实例):

$original = 'foo'
$potentiallyModified = $original -replace 'x', 'y'

# Test for reference equality.
$replacementWasMade = -not [object]::ReferenceEquals($original, $potentiallyModified)

您的特定用例:

您必须使用$content = (Get-Content -Raw $fileFullName),即-Raw开关,以单个字符串的形式读取输入文件 并执行{{ 1}}对那个单个字符串进行操作。

否则,您将获得一个数组行,以及-replace changes 的行为与一个数组值的LHS一起执行 filtering < / em>而不是返回布尔值。

此外,您的-eq RHS也将是一个数组(具有可能被修改的行的行的数组),该数组被强制为单个字符串,且元素之间用空间,这意味着它无法按预期运行:

-eq

也就是说,RHS被强制为单个字符串'foo bar',而这两个字符串都不匹配LHS元素,因此返回了空数组

关于效果

要加快文件I / O速度,请完全避免使用cmdlet并直接使用.NET类型:

PS> 'foo', 'bar' -eq 'foo', 'bar'
 # !! NO OUTPUT

答案 1 :(得分:0)

尝试进行比较:

$xmlContent = New-Object System.Xml.XmlDocument
$xmlContent = [xml]([System.IO.File]::ReadLines($filePath))

$header     = $xmlContent.xml
$headerNew  = $xmlContent.xml -replace 'version="(\d+\.\d+)" encoding="(.+)"', 'version="$1" encoding="UTF-8"' 

if( $header -ne $headerNew ) {
    $xmlContent.xml    = $headerNew
    $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
    [void][System.IO.File]::WriteAllLines($filePath, $xmlContent.OuterXml, $Utf8NoBomEncoding)
}