Ich muss in Windows 7 die letzten n Zeilen mit großen Dateien (1 bis 4 GB) abrufen. Aufgrund von Unternehmensbeschränkungen kann ich keinen Befehl ausführen, der nicht integriert ist. Das Problem ist, dass alle Lösungen, die ich gefunden habe, die gesamte Datei zu lesen scheinen, so dass sie extrem langsam sind.
Kann dies schnell erreicht werden?
Anmerkungen:
Lösungen hier nix-Tail-Äquivalent-Befehl in Windows Powershell hat nicht funktioniert. Mit -wait
macht es nicht schnell. Ich habe nicht -tail
(und ich weiß nicht, ob es schnell gehen wird).
PS: Es gibt einige verwandte Fragen zu head
und tail
, die sich jedoch nicht mit dem Thema Geschwindigkeit befassen. Daher sind nützliche oder akzeptierte Antworten hier möglicherweise nicht hilfreich. Z.B.,
Windows-Entsprechung des Befehls 'tail'
CMD.EXE-Batch-Skript zum Anzeigen der letzten 10 Zeilen aus einer txt-Datei
Extrahieren Sie N Zeilen aus der Datei mit einem einzigen Windows-Befehl
Powershell, um die ersten x MB einer Datei zu erhalten
https://superuser.com/questions/859870/windows-equivalent-of-the-head-c-command
Wie wäre es damit (liest die letzten 8 Bytes für die Demo):
$fpath = "C:\10GBfile.dat"
$fs = [IO.File]::OpenRead($fpath)
$fs.Seek(-8, 'End') | Out-Null
for ($i = 0; $i -lt 8; $i++)
{
$fs.ReadByte()
}
UPDATE . Um Bytes als Zeichenfolge zu interpretieren (aber achten Sie darauf, dass Sie die richtige Codierung auswählen - hier wird UTF8 verwendet):
$N = 8
$fpath = "C:\10GBfile.dat"
$fs = [IO.File]::OpenRead($fpath)
$fs.Seek(-$N, [System.IO.SeekOrigin]::End) | Out-Null
$buffer = new-object Byte[] $N
$fs.Read($buffer, 0, $N) | Out-Null
$fs.Close()
[System.Text.Encoding]::UTF8.GetString($buffer)
PDATE 2. Um die letzten M Zeilen zu lesen, lesen wir die Datei nach Teilen, bis das Ergebnis mehr als M Zeilenumbrüche enthält:
$M = 3
$fpath = "C:\10GBfile.dat"
$result = ""
$seq = "`r`n"
$buffer_size = 10
$buffer = new-object Byte[] $buffer_size
$fs = [IO.File]::OpenRead($fpath)
while (([regex]::Matches($result, $seq)).Count -lt $M)
{
$fs.Seek(-($result.Length + $buffer_size), [System.IO.SeekOrigin]::End) | Out-Null
$fs.Read($buffer, 0, $buffer_size) | Out-Null
$result = [System.Text.Encoding]::UTF8.GetString($buffer) + $result
}
$fs.Close()
($result -split $seq) | Select -Last $M
Versuchen Sie, mit größerem $buffer_size
Zu spielen - dies entspricht im Idealfall der erwarteten durchschnittlichen Zeilenlänge, um weniger Festplattenoperationen durchzuführen. Achten Sie auch auf $ seq - dies kann \r\n
Oder nur \n
Sein. Dies ist sehr schmutziger Code ohne Fehlerbehandlung und Optimierungen.
Wenn Sie über PowerShell 3 oder höher verfügen, können Sie den Parameter -Tail
Für Get-Content
Verwenden, um die letzten n
Zeilen abzurufen.
Get-content -tail 5 PATH_TO_FILE;
In einer 34-MB-Textdatei auf meiner lokalen SSD wurde dies in 1 Millisekunde gegenüber 8,5 Sekunden für get-content |select -last 5
Zurückgegeben.
Mit die großartige Antwort von Aziz Kabyshev , die das Problem der Geschwindigkeit löst, und mit ein wenig googeln habe ich dieses Skript verwendet
$fpath = $Args[1]
$fs = [IO.File]::OpenRead($fpath)
$fs.Seek(-$Args[0], 'End') | Out-Null
$mystr = ''
for ($i = 0; $i -lt $Args[0]; $i++)
{
$mystr = ($mystr) + ([char[]]($fs.ReadByte()))
}
$fs.Close()
Write-Host $mystr
das rufe ich aus einer batchdatei mit
@PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '.\myscript.ps1' %1 %2"
(Dank an Ausführen eines PowerShell-Skripts aus einer Batchdatei ).
Dies ist keine Antwort, sondern ein großer Kommentar als Antwort auf die Antwort von sancho.s.
Wenn Sie kleine PowerShell-Skripte aus einer Batch-Datei verwenden möchten, empfehlen wir Ihnen die folgende Methode, die einfacher ist und es ermöglicht, den gesamten Code in derselben Batch-Datei zu belassen:
@PowerShell ^
$fpath = %2; ^
$fs = [IO.File]::OpenRead($fpath); ^
$fs.Seek(-%1, 'End') ^| Out-Null; ^
$mystr = ''; ^
for ($i = 0; $i -lt %1; $i++) ^
{ ^
$mystr = ($mystr) + ([char[]]($fs.ReadByte())); ^
} ^
Write-Host $mystr
%End PowerShell%