以下の PowerShell コードを最適化する方法はありますか (複数のテキスト ファイルに含まれる文字列によって特定の行を grep し、1 つの行にまとめます)。
$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 のテキスト ファイル (合計 200 GB 以上) では、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 も得意ではないので、上記のスクリプトはすべて Google で検索してコピー アンド ペーストして作成しました。
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 テキスト ファイルに追加されます。また、Line プロパティだけでなく、Filename、Path、LineNumber プロパティを使用して、一致した行のファイル名や行番号を取得することもできます。
多数のファイルに対して実行されるスクリプトをテストしたいが、すべてのファイルのチェックが完了するまで待機したくない場合は、Select-Object コマンドレットを使用して、チェックするファイルの数を制限できます。
例:
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 を実行する方がかなり高速です。
Select-String "0xAC1CC07A" -Path $filenm.FullName | Add-Content $OutFile
希望どおりに出力できるよう、ファイルに追加する前に必ず出力を確認してください。
速度に関しては、Get-ChildItem は PowerShell では非常に遅いことで有名です (PowerShell はオブジェクトのテキスト表現ではなくオブジェクトをフェッチするため)。これにはさまざまな回避策があります。
ただし、コード内の Get-ChildItem 行は最適化できます。私が見た限りでは、通常のコンシューマーグレードの 7.2k HDD では、フィルターを使用すると、includes/excludes を使用するよりも約 3.5 倍高速です。
Get-ChildItem -Path "D:\FILES" -Filter "ubuntlive1mb_?????_2015090101*.txt" -Recurse -Force
私の記憶が正しければ、PowerShell の以前のバージョンでは、フィルターに関していくつか問題がありました。たとえば、拡張子が .htm のすべてのファイルを対象にすると、拡張子が .html のファイルも取得されてしまう (フィルターを適用したのに*.htm*
を適用していないかのように*.htm
) ので、その点には注意したほうがよいでしょう。