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

zbwhf8kr  于 10个月前  发布在  Shell
关注(0)|答案(2)|浏览(139)

我需要在发送日志文件给供应商分析之前对它们进行编辑。由于我支持的平台的动态特性,我必须动态生成列表。这一点很好。
例如,我生成了一个CSV文件,大约有500行,看起来像:

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

字符串
我将此文件用作$redactions = Import-Csv $nodeRedactions
该脚本运行通过编校文件来获取查找和替换对,然后在目标文件上执行查找/替换。例如,Server 1被替换为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秒。有更快的方法吗?

hxzsmxv2

hxzsmxv21#

我建议您使用哈希表在NodeRedaction值之间进行快速查找,并结合正则表达式模式和匹配评估器,或者使用PowerShell 7+中的脚本块进行替换,使用此哈希表进行替换。

$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

wkftcu5l

wkftcu5l2#

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

$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

字符串

相关问题