it-swarm.com.de

Holen Sie sich die letzten n Zeilen oder Bytes einer riesigen Datei in Windows (wie Unix's Tail). Vermeiden Sie zeitaufwändige Optionen

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:

  1. Ich habe es geschafft, die ersten n Zeilen schnell zu bekommen.
  2. Es ist in Ordnung, wenn ich die letzten n Bytes bekomme. (Ich habe dies https://stackoverflow.com/a/18936628/2707864 für die ersten n Bytes verwendet.).

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

https://serverfault.com/questions/490841/how-to-display-the-irst-n-lines-of-a-command-output-in-windows-the-equivalent

Powershell, um die ersten x MB einer Datei zu erhalten

https://superuser.com/questions/859870/windows-equivalent-of-the-head-c-command

17
sancho.s

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.

14
Aziz Kabyshev

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.

47
alroc

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 ).

3
sancho.s

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%
1
Aacini