执行 *nix grep 的 PowerShell 模拟

执行 *nix grep 的 PowerShell 模拟

有没有办法优化下面的 PowerShell 代码(它通过从一堆文本文件中包含的字符串将特定行合并到一个文件中):

$ErrorActionPreference = "Continue"
Start-Transcript -path D:\0xAC1CC07A.log -append
$OutFile = "D:\0xAC1CC07A.txt"
echo "filtering 0xAC1CC07A"
ForEach ($filenm in ((get-childitem -Path D:\FILES\* -include ubuntlive1mb_?????_201509*.txt -recurse -force))) 
{
 $filenm.fullName;
 (Get-Content $filenm) | select-string "0xAC1CC07A" | Add-Content $OutFile
}
Stop-Transcript

它在小型工作负载上表现良好,但在 160K 文本文件(总共超过 200GB)上,它在我的 Win2008R2 VM 上运行了 4 天以上。令人惊讶的是,类似虚拟硬件上的 Ubuntu 14.04 在 4 小时内就完成了这项工作:

grep --no-filename "0xac1cc07a" ./FILES/ubuntlive1mb_?????_201509*.txt >>./0xAC1CC07A.txt

或者更准确地说:

find ./FILES -name "ubuntlive1mb_?????_201509*.txt" -type f -print0 | xargs -0 grep --no-filename "0xac1cc07a" $1 >>./0xAC1CC07A.txt

我既不擅长 PowerShell,也不擅长 *nix,所有以上脚本都是通过谷歌搜索和复制粘贴创建的。

Windows 系统已通过禁用列表中的 dos 文件名和目录更新对文件系统进行了优化。Ubuntu 开箱即用。

答案1

这个非常简单的 Powershell 脚本应该可以完成您要做的事情:

$OutFile = "D:\0xAC1CC07A.txt"
Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt -Recurse | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

这只会将匹配的行添加到 $OutFile 文本文件中。您还可以使用它来获取文件名或匹配行的行号,方法是使用 Filename、Path 和 LineNumber 属性,而不仅仅是 Line 属性。

如果您想测试一个针对许多文件运行的脚本,但又不想等待它完成检查所有文件,那么您可以使用 Select-Object cmdlet 来限制它将检查的文件数量。

例子:

Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt | Select-Object -First 100 | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

这将仅针对 Get-ChildItem 返回的前 100 个文本文件运行上述脚本。

答案2

您将得到略有不同的输出(但如果有需要,可以处理),但从我所看到的情况来看,直接在文件上选择字符串而不是先获取文件内容要快得多。

Select-String "0xAC1CC07A" -Path $filenm.FullName | Add-Content $OutFile

只需记住在将输出附加到文件之前先检查输出,这样您就可以按照所需的方式获取它。

至于速度;Get-ChildItem 在 PowerShell 中非常慢(因为 PowerShell 喜欢获取对象而不仅仅是对象的文本表示),并且对此有各种解决方法。

但是,代码中的 Get-ChildItem 行可以进行优化。据我所知,在普通消费级 7.2k HDD 上使用 Filter 比使用 includes/excludes 大约快 3.5 倍。

Get-ChildItem -Path "D:\FILES" -Filter "ubuntlive1mb_?????_2015090101*.txt" -Recurse -Force

如果我没记错的话,早期版本的 PowerShell 在过滤方面存在一些问题,比如如果您想要所有扩展名为 .htm 的文件,它也会选择扩展名为 .html 的文件(就好像您已经过滤过*.htm*一样*.htm),所以你可能要留意这一点。

相关内容