Bal*_*gle 2 csv powershell replace find
我需要在将日志文件发送给供应商进行分析之前对其进行编辑。由于我支持的平台的动态特性,我必须动态生成列表。这一点还好。
例如,我生成一个大约有 500 行的 CSV 文件,如下所示:
"Node","Redaction"
"Server1","Redacted-Node-1"
"Server2.domain.local","Redacted-Node-2"
"Server3","Redacted-Node-3"
etc
Run Code Online (Sandbox Code Playgroud)
我使用这个文件作为$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
Run Code Online (Sandbox Code Playgroud)
这对于小文件来说非常有效。但是,当在具有 50,000 行的文件上运行时,对每行运行查找和替换大约需要 1 秒。有没有更快的方法?
我建议您结合正则表达式模式和匹配评估器使用哈希表在Node和值之间进行快速查找,或者使用 PowerShell 7+ 中的脚本块进行替换,该脚本块使用此哈希表进行替换。Redaction
$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
Run Code Online (Sandbox Code Playgroud)
$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
Run Code Online (Sandbox Code Playgroud)
由于评论中的反馈,添加此替代方案可以使用匿名函数进一步提高性能:
$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
Run Code Online (Sandbox Code Playgroud)
不要将整个文件读入内存,然后尝试替换所有字符串中的每个节点名称,而是将其翻转,以便一次只读取 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
Run Code Online (Sandbox Code Playgroud)