Ausführen eines PowerShell-Analogons von *nix grep

Ausführen eines PowerShell-Analogons von *nix grep

Gibt es eine Möglichkeit, den folgenden PowerShell-Code zu optimieren (er fasst bestimmte Zeilen aus mehreren Textdateien nach Zeichenfolgen zu einer einzigen zusammen):

$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

Bei kleinen Arbeitslasten funktioniert es gut, aber bei 160.000 Textdateien (insgesamt über 200 GB) läuft es auf meiner Win2008R2-VM mehr als 4 Tage. Überraschenderweise erledigte Ubuntu 14.04 auf der gleichen virtuellen Hardware den Job innerhalb von 4 Stunden:

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

Oder genauer:

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

Ich bin weder gut in PowerShell noch in *nix, alle oben genannten Skripte wurden durch Googeln und Kopieren und Einfügen erstellt.

Das Dateisystem der Windows-Box wurde optimiert, indem DOS-Dateinamen und Verzeichnisaktualisierungen in der Liste deaktiviert wurden. Ubuntu wurde einfach sofort installiert.

Antwort1

Dieses sehr einfache Powershell-Skript sollte das tun, was Sie suchen:

$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 }

Dadurch werden nur die übereinstimmenden Zeilen in die Textdatei $OutFile eingefügt. Sie können dies auch verwenden, um die Dateinamen oder die Zeilennummern der übereinstimmenden Zeilen abzurufen, indem Sie die Eigenschaften Filename, Path und LineNumber anstelle nur der Eigenschaft Line verwenden.

Wenn Sie ein Skript testen möchten, das für viele Dateien ausgeführt wird, aber nicht warten möchten, bis die Überprüfung aller Dateien abgeschlossen ist, können Sie die Zahl der zu überprüfenden Dateien mit dem Cmdlet „Select-Object“ begrenzen.

Beispiel:

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 }

Dadurch wird das obige Skript nur für die ersten 100 Textdateien ausgeführt, die von Get-ChildItem zurückgegeben werden.

Antwort2

Sie erhalten eine leicht unterschiedliche Ausgabe (darum kann man sich aber kümmern, falls Bedarf besteht), aber meiner Erfahrung nach ist es viel schneller, wenn man den Select-String direkt in der Datei ausführt, anstatt zuerst den Dateiinhalt abzurufen.

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

Denken Sie daran, die Ausgabe zuerst zu überprüfen, bevor Sie sie an die Datei anhängen, damit Sie sie in der gewünschten Form erhalten.

Was die Geschwindigkeit betrifft: Get-ChildItem ist in PowerShell notorisch langsam (da PowerShell lieber Objekte abruft als nur die Textdarstellung von Objekten) und es gibt verschiedene Workarounds dafür.

Die Get-ChildItem-Zeile in Ihrem Code kann jedoch optimiert werden. Soweit ich gesehen habe, ist die Verwendung von Filter etwa 3,5-mal schneller als die Verwendung von Includes/Excludes auf einer normalen 7,2-k-Festplatte für Verbraucher.

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

Wenn ich mich recht erinnere, gab es in früheren Versionen von PowerShell einige Probleme mit Filtern. Wenn Sie beispielsweise alle Dateien mit der Erweiterung .htm wollten, wurden auch die Dateien mit der Erweiterung .html erkannt (als ob Sie gefiltert hätten *.htm*und nicht *.htm). Darauf sollten Sie also achten.

verwandte Informationen