如何查明“-replace”是否确实做了什么

Nei*_*ell 3 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."
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 6

有人指出,正确的 XML 解析通常是更可取的,但您已经澄清,您需要保留输入文件的精确格式,以便稍后进行无干扰的比较。


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

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

在最简单的情况下:

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

$replacementWasMade = $original -cne $potentiallyModified
Run Code Online (Sandbox Code Playgroud)

笔记:

  • -cne而不是-ne用于执行区分大小写比较,还可以检测替换仅更改输入字符串的大小写的情况。

  • 可以想象,可能已经执行了有效的无操作'foo' -replace 'o', 'o'替换(例如, ),但上面没有检测到;也就是说,虽然[string]在这种情况下返回一个新实例,但这通常并不重要,因为字符串通常是通过相等而不是引用相等进行比较 - 见下文。

如果在这种情况下性能至关重要 - 我怀疑它在大多数情况下都很重要 - 您可以应用以下微优化,利用(已记录的)事实:如果指定的正则表达式不匹配,则输入字符串将返回为-是(同一[string]实例):

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

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

您的具体用例:

您必须使用$content = (Get-Content -Raw $fileFullName),即-Raw开关将输入文件作为单个字符串读取并-replace对该单个字符串执行操作。

否则,您将得到一个行数组,并且-eq 更改数组值 LHS 的 行为以执行LHS过滤而不是返回布尔值。

此外,您的-eqRHS 也将是一个数组(具有可能修改的行的行数组),它被强制为单个字符串,其中元素由空格分隔,这意味着它不会按预期工作:

PS> 'foo', 'bar' -eq 'foo', 'bar'
 # !! NO OUTPUT
Run Code Online (Sandbox Code Playgroud)

也就是说,RHS 被强制为单个字符串“foo bar”,它与 LHS 元素都不匹配,因此返回一个空数组。

至于性能

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

$content = [IO.File]::ReadAllText($fileFullName)
Run Code Online (Sandbox Code Playgroud)