Powershell:寻找一种使用 csv 文件作为输入对文本文件进行查找和替换的快速方法

Powershell: Looking for a fast way to do a find & replace on a text file using a csv file as the input

提问人:BaldFeegle 提问时间:11/17/2023 更新时间:11/18/2023 访问量:45

问:

我要求在将日志文件发送给供应商分析之前对其进行编辑。由于我支持的平台的动态性质,我必须动态生成列表。这点很好。

例如,我生成了一个大约有 500 行的 CSV 文件,如下所示:

"Node","Redaction"
"Server1","Redacted-Node-1"
"Server2.domain.local","Redacted-Node-2"
"Server3","Redacted-Node-3"
etc

我使用这个文件作为$redactions = Import-Csv $nodeRedactions

该脚本通过编校文件运行以获取查找和替换对,然后对目标文件执行查找/替换。例如,将 Server1 替换为 Redacted-Node-1。

$fullpath 是当前正在使用以下代码处理的文本文件的路径:

$redactions = Import-Csv $nodeRedactions 
$fileContent = Get-Content $fullpath
$n = 1
foreach ($row in $redactions)

{ 
    #Write-Host $n + " " + $fullpath
    $field1 = $row.Node 
    $field2 = $row.Redaction
    $fileContent = $fileContent | Foreach-Object { $_ -replace $field1,$field2}
    #$n= $n + 1 
}
#Create the output file complete with redactions
$fileContent | Out-File $outputFile

这非常适合小文件。但是,当在具有 50,000 行的文件上运行时,对每一行运行查找和替换大约需要 1 秒钟。有没有更快的方法?

PowerShell CSV 替换 查找

评论


答:

5赞 Santiago Squarzon 11/17/2023 #1

我建议将哈希表与正则表达式模式和匹配计算器结合使用,以便在 和 值之间快速查找,或者在 PowerShell 7+ 中使用脚本块进行替换,该脚本块使用此哈希表进行替换。NodeRedaction

$map = @{}
Import-Csv $nodeRedactions | ForEach-Object {
    $map[$_.Node] = $_.Redaction
}

$re = [regex]::new(
    '(?:{0})' -f ($map.Keys.ForEach({ [regex]::Escape($_) }) -join '|'),
    [System.Text.RegularExpressions.RegexOptions] 'Compiled, IgnoreCase')

$content = Get-Content $fullPath -Raw
$re.Replace($content, { $map[$args[0].Value] }) | Set-Content $outputFile

# NOTE: In PowerShell 7+ you can use:
(Get-Content $fullPath -Raw) -replace $re, { $map[$_.Value] } |
    Set-Content $outputFile

值得注意的是,上述方法将在替换内存并将其存储在内存中之前获取内存中的内容,如果您需要保留内存,那么对于逐行处理,我建议:$fullPath$outputFile

$map = @{}
Import-Csv $nodeRedactions | ForEach-Object {
    $map[$_.Node] = $_.Redaction
}

$re = [regex]::new(
    '(?:{0})' -f ($map.Keys.ForEach({ [regex]::Escape($_) }) -join '|'),
    [System.Text.RegularExpressions.RegexOptions] 'Compiled, IgnoreCase')

[System.IO.File]::ReadLines($fullPath) |
    ForEach-Object { $re.Replace($_, { $map[$args[0].Value] }) } |
    Set-Content $outputFile

# NOTE: In PowerShell 7+ you can use:
[System.IO.File]::ReadLines($fullPath) |
    ForEach-Object { $_ -replace $re, { $map[$_.Value] } } |
    Set-Content $outputFile

由于评论中的反馈,添加此替代方法应该会使用匿名函数进一步提高性能:

[System.IO.File]::ReadLines($fullPath) | & {
    begin {
        $map = @{}
        Import-Csv $nodeRedactions | ForEach-Object {
            $map[$_.Node] = $_.Redaction
        }

        $re = [regex]::new(
            '(?:{0})' -f ($map.Keys.ForEach({ [regex]::Escape($_) }) -join '|'),
            [System.Text.RegularExpressions.RegexOptions] 'Compiled, IgnoreCase')
    }

    process {
        # NOTE: In PowerShell 7+ you can use:
        # $_ -replace $re, { $map[$_.Value] }

        $re.Replace($_, { $map[$args[0].Value] })
    }
} | Set-Content $outputFile

评论

1赞 Santiago Squarzon 11/18/2023
@MathiasR.Jessen好点,更新,谢谢
0赞 BaldFeegle 11/28/2023
这比我做的方式快得多,但仍然很慢。在生产数据上,大约有 3500 个编辑需要检查,如果发现,则进行替换。在 4000 行的文件中,大约需要 8.5 分钟,逐行方法略快。这和我希望的一样快吗?
1赞 Santiago Squarzon 11/28/2023
您确定使用的第一种方法较慢吗?它不应该,但它肯定会消耗更多的内存@BaldFeegle否则我看不到太多改进的空间,它本身很慢,但是您可以通过用 C 重写代码来提高性能#-RawRegex.ReplaceMatchEvaluator
0赞 BaldFeegle 11/28/2023
使用相同的输入文件计时 - Raw 在 8 分 32 秒时进入,逐行在 8 分 02 秒。我想我可以尝试使用 C#,但这首先需要学习 C#,并在我们的环境中找到运行它的地方。
1赞 BaldFeegle 11/30/2023
感谢您对此的所有帮助,遗憾的是,最新版本是迄今为止我的测试文件中最慢的。不过,在你的帮助下,我得到了比我自己制作的东西至少快 10 倍的东西。非常感谢。
5赞 Mathias R. Jessen 11/18/2023 #2

与其将整个文件读入内存,然后尝试替换所有字符串中的每个节点名称,不如将其翻转,以便一次只读取 1 行,然后在将其写入磁盘之前对其执行所有可能的替换:

$redactions = Import-Csv $nodeRedactions 

Get-Content $fullpath |ForEach-Object {
  foreach ($row in $redactions) {
    # make all the required substitutions
    $_ = $_ -replace $([regex]::Escape($row.Node)),$row.Redaction
  }
  # output to the pipeline
  $_
} | Out-File $outputFile

评论

0赞 BaldFeegle 11/28/2023
谢谢,不幸的是,我在实现此操作时出现内存不足错误。引发了类型为“System.OutOfMemoryException”的异常。在 D:\PS_Scripts\Autoredact\AutoRedact_28_11_2023_2.ps1:154 char:24 + $_ = $_ -replace $([regex]::Escape($row.节点)),$row。密文 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo:OperationStopped:(:) [],OutOfMemoryException + FullyQualifiedErrorId:System.OutOfMemoryException
0赞 BaldFeegle 11/28/2023
选中 powershell 以允许最大内存。