Unix-Tail-Äquivalent-Befehl in Windows Powershell

349

Ich muss mir die letzten Zeilen einer großen Datei ansehen (typische Größe ist 500 MB-2 GB). Ich suche nach einem Äquivalent des Unix-Befehls tailfür Windows Powershell. Einige Alternativen sind:

http://tailforwin32.sourceforge.net/

und

Get-Content [Dateiname] | Select-Object -Last 10

Für mich ist es nicht erlaubt, die erste Alternative zu verwenden, und die zweite Alternative ist langsam. Kennt jemand eine effiziente Implementierung von Tail für PowerShell?

mutelogan
quelle
2
Wie können wir wissen, ob Sie das verwenden dürfen, was wir vorschlagen, wenn Sie nicht sagen, warum Sie die erste Alternative nicht verwenden dürfen?
Gabe
3
Gibt es einen Grund, warum Sie den tailin sourceforge.net/projects/unxutils/files/unxutils/current/… bereitgestellten Befehl nicht verwenden können ?
Gabe
1
Dies ist in einer Produktionsmaschine, in der ich keine externen ausführbaren Dateien kopieren durfte. Einige seltsame Richtlinien. :) Kann nicht anders. Danke für den Unxutils Link.
Mutelogan
https://devcentral.f5.com/blogs/us/unix-to-powershell-tail demonstriert die reine PoSH-Implementierung.
Jewgenij
Keine Notwendigkeit, Select-Object zu verwenden: Get-Content [filename] -last 10und -tailfür -f
MortenB

Antworten:

492

Verwenden Sie den -waitParameter mit Get-Content, der Zeilen anzeigt, wenn sie der Datei hinzugefügt werden. Diese Funktion war in PowerShell v1 vorhanden, wurde jedoch aus irgendeinem Grund in v2 nicht gut dokumentiert.

Hier ist ein Beispiel

Get-Content -Path "C:\scripts\test.txt" -Wait

Sobald Sie dies ausführen, aktualisieren und speichern Sie die Datei und Sie werden die Änderungen auf der Konsole sehen.

Ravikanth
quelle
16
Interessant. Ich hätte gedacht, dass alle vorhandenen Argumente auch in der Hilfe erscheinen, man gc -par waitsagt mir aber, dass es keinen Parameter gibt. Aber ich denke, dies löst nicht das Problem, das das OP hat, da es darum gebeten hat tail, nicht tail -fund auch eine effiziente Implementierung. Da dieser auch die gesamte Datei liest, bevor die letzten Zeilen zurückgegeben werden, ist dies für die erwarteten Dateigrößen schmerzhaft.
Joey
5
Zu Ihrer Information, dies ist, was die Get-FileTail-Implementierung (Alias ​​Tail) in PSCX tut. Wenn Sie neugierig sind, können Sie sich den Quellcode ansehen
Keith Hill
7
@Joey -Wait ist ein dynamischer Parameter, der nur für den FileSystem-Anbieter gilt. GC kann auf jedem Anbieter verwendet werden, der diese API implementiert. Die einzige Möglichkeit neben der Dokumentation, die ich kenne, um diese zu entdecken, ist die Verwendung von (gcm Get-Content) .Parametern aus dem entsprechenden Anbieterpfad. Verwenden Sie nicht den Alias ​​"gc", da die dynamischen Parameter nicht angezeigt werden.
JasonMArcher
11
Ich weiß, dass es eine Weile her ist, aber dies erfordert, dass der Prozess, der in die Datei schreibt, geöffnet, angehängt und geschlossen wird, bevor Get-Content funktioniert. Wenn der Schreibvorgang die Datei nie schließt, funktioniert er nicht, was bei tail -f nicht der Fall ist.
David Newcomb
15
Seltsamerweise zeigt -Wait mir nur neue Zeilen an, wenn ich auf irgendeine Weise auf eine Protokolldatei zugreife (z. B. wenn ich sie im Windows Explorer auswähle). Tail bietet Updates, wenn neue Zeilen in meine Datei geschrieben werden. Mit -Wait kann ich ein PowerShell-Fenster geöffnet lassen, in dem keine neuen Zeilen angezeigt werden, während die Datei geschrieben wird. Wenn ich dann im Windows Explorer vorbeischaue und auf die Datei klicke, "wacht" PowerShell plötzlich auf und holt die verbleibenden Zeilen ein. Ist das ein Fehler?
JoshL
197

Der Vollständigkeit halber werde ich erwähnen, dass Powershell 3.0 jetzt ein -Tail-Flag auf Get-Content hat

Get-Content ./log.log -Tail 10

Ruft die letzten 10 Zeilen der Datei ab

Get-Content ./log.log -Wait -Tail 10

Ruft die letzten 10 Zeilen der Datei ab und wartet auf weitere

Auch für den * nix - Benutzer zu beachten , dass die meisten Systeme alias Katze auf Get-Content, so dass diese in der Regel Werke

cat ./log.log -Tail 10
George Mauer
quelle
@LauraLiparulo Wie funktioniert das nicht? Ich habe es definitiv schon einmal benutzt.
George Mauer
4
Ich habe es gerade benutzt und es hat genau in diesem Format funktioniertGet-Content .\test.txt -Wait -Tail 1
Coops
@LauraLiparulo - Funktioniert auch für mich:Get-Content -Path .\sync.log -Wait -Tail 10
Elika Kohen
Auf ISE habe ich while ($ true) / sleep verwendet und zu diesem gewechselt, aber dieser sperrt auch die gesamte ISE und kann keine Skripte auf anderen Registerkarten ausführen. Soll ich einfach eine neue ISE-Instanz starten?
Teoman Shipahi
@Teomanshipahi Inwiefern hat der -WaitParameter bei Ihnen nicht funktioniert?
George Mauer
116

Ab PowerShell Version 3.0 verfügt das Cmdlet Get-Content über einen -Tail- Parameter, der hilfreich sein sollte. Siehe die TechNet - Bibliothek Microsoft Online - Hilfe für Get-Content.

Dan Blanchard
quelle
1
Link zum Herunterladen hier - microsoft.com/en-us/download/details.aspx?id=34595 .
Gedrox
4
Hinweis für einige - PS 3.0 ist für Windows XP und Vista nicht verfügbar.
tjmoore
1
Ich benutze die von Dan erwähnte Technik, aber ich nehme sie in meinem $ PROFILE auf. Öffnen Sie es mit dem Editor $ PROFILE. Erstellen Sie dann im Textdokument eine neue Funktion: function Tail ($ path) {Get-content -tail 15 -path $ path -wait} Auf diese Weise können Sie bei jedem Start von PowerShell auf die Funktion zugreifen.
Jake Nelson
Dies sollte die akzeptierte Antwort sein. - Das in der aktuell akzeptierten Antwort erwähnte Warteflag funktioniert nicht mehr.
Abdullah Leghari
21

Ich habe einige der hier gegebenen Antworten verwendet, aber nur einen Hinweis darauf

Get-Content -Path Yourfile.log -Tail 30 -Wait 

wird nach einer Weile die Erinnerung zerkauen. Ein Kollege hat am letzten Tag einen solchen "Schwanz" hinterlassen und er stieg auf 800 MB. Ich weiß nicht, ob sich Unix Tail genauso verhält (aber ich bezweifle es). Es ist also in Ordnung, es für kurzfristige Anwendungen zu verwenden, aber seien Sie vorsichtig damit.

John Lockwood
quelle
18

PowerShell Community Extensions (PSCX) stellt das Get-FileTailCmdlet bereit . Es sieht nach einer geeigneten Lösung für die Aufgabe aus. Hinweis: Ich habe es nicht mit extrem großen Dateien versucht, aber die Beschreibung besagt, dass es den Inhalt effizient zusammenfasst und für große Protokolldateien ausgelegt ist.

NAME
    Get-FileTail

SYNOPSIS
    PSCX Cmdlet: Tails the contents of a file - optionally waiting on new content.

SYNTAX
    Get-FileTail [-Path] <String[]> [-Count <Int32>] [-Encoding <EncodingParameter>] [-LineTerminator <String>] [-Wait] [<CommonParameters>]

    Get-FileTail [-LiteralPath] <String[]> [-Count <Int32>] [-Encoding <EncodingParameter>] [-LineTerminator <String>] [-Wait] [<CommonParameters>]

DESCRIPTION
    This implentation efficiently tails the cotents of a file by reading lines from the end rather then processing the entire file. This behavior is crucial for ef
    ficiently tailing large log files and large log files over a network.  You can also specify the Wait parameter to have the cmdlet wait and display new content
    as it is written to the file.  Use Ctrl+C to break out of the wait loop.  Note that if an encoding is not specified, the cmdlet will attempt to auto-detect the
     encoding by reading the first character from the file. If no character haven't been written to the file yet, the cmdlet will default to using Unicode encoding
    . You can override this behavior by explicitly specifying the encoding via the Encoding parameter.
Roman Kuzmin
quelle
1
In der aktuellen Version gibt es einen Fehler, der in täglichen Schritten behoben wird. Ich würde empfehlen, die neuesten Bits zu greifen und sie zumindest zu kompilieren, bis eine aktualisierte Version veröffentlicht wird.
Keith Hill
7
Die Version 2.0 braucht Ewigkeiten, um die 10 letzten Zeilen einer 1-GB-CSV-Datei anzuzeigen, und Get-Content [filename] | Select-Object -Last 10kann nicht abgebrochen werden
Jader Dias
15

Nur einige Ergänzungen zu früheren Antworten. Für Get-Content sind Aliase definiert, z. B. wenn Sie an UNIX gewöhnt sind cat, und es gibt auch typeund gc. Also statt

Get-Content -Path <Path> -Wait -Tail 10

Du kannst schreiben

# Print whole file and wait for appended lines and print them
cat <Path> -Wait
# Print last 10 lines and wait for appended lines and print them
cat <Path> -Tail 10 -Wait
Mikael Sundberg
quelle
3

Mit Powershell V2 und niedriger liest get-content die gesamte Datei, sodass es für mich keinen Nutzen hatte. Der folgende Code funktioniert für das, was ich brauchte, obwohl es wahrscheinlich einige Probleme mit der Zeichencodierung gibt. Dies ist effektiv tail -f, kann jedoch leicht geändert werden, um die letzten x Bytes oder die letzten x Zeilen abzurufen, wenn Sie rückwärts nach Zeilenumbrüchen suchen möchten.

$filename = "\wherever\your\file\is.txt"
$reader = new-object System.IO.StreamReader(New-Object IO.FileStream($filename, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [IO.FileShare]::ReadWrite))
#start at the end of the file
$lastMaxOffset = $reader.BaseStream.Length

while ($true)
{
    Start-Sleep -m 100

    #if the file size has not changed, idle
    if ($reader.BaseStream.Length -eq $lastMaxOffset) {
        continue;
    }

    #seek to the last max offset
    $reader.BaseStream.Seek($lastMaxOffset, [System.IO.SeekOrigin]::Begin) | out-null

    #read out of the file until the EOF
    $line = ""
    while (($line = $reader.ReadLine()) -ne $null) {
        write-output $line
    }

    #update the last max offset
    $lastMaxOffset = $reader.BaseStream.Position
}

Ich habe den größten Teil des Codes dafür hier gefunden .

Hajamie
quelle
1
Stimmt es, dass Get-Content mit der Option -Tail die gesamte Datei liest? Bei großen Dateien scheint es mir in Ordnung zu sein.
Govert
Ich glaube es kommt auf die PS Version an. Ich habe die Antwort aktualisiert. Ich steckte auf einem Server fest, ohne die Möglichkeit zu haben, irgendetwas zu installieren, daher war der obige Code nützlich.
Hajamie
3

Ich nahm die Lösung von @ hajamie und wickelte sie in einen etwas bequemeren Skript-Wrapper ein.

Ich habe eine Option hinzugefügt, um von einem Versatz vor dem Ende der Datei zu beginnen, damit Sie die Schwanz-ähnliche Funktionalität verwenden können, um einen bestimmten Betrag vom Ende der Datei zu lesen. Beachten Sie, dass der Offset in Bytes und nicht in Zeilen angegeben ist.

Es besteht auch die Möglichkeit, weiter auf weitere Inhalte zu warten.

Beispiele (vorausgesetzt, Sie speichern dies als TailFile.ps1):

.\TailFile.ps1 -File .\path\to\myfile.log -InitialOffset 1000000
.\TailFile.ps1 -File .\path\to\myfile.log -InitialOffset 1000000 -Follow:$true
.\TailFile.ps1 -File .\path\to\myfile.log -Follow:$true

Und hier ist das Drehbuch selbst ...

param (
    [Parameter(Mandatory=$true,HelpMessage="Enter the path to a file to tail")][string]$File = "",
    [Parameter(Mandatory=$true,HelpMessage="Enter the number of bytes from the end of the file")][int]$InitialOffset = 10248,
    [Parameter(Mandatory=$false,HelpMessage="Continuing monitoring the file for new additions?")][boolean]$Follow = $false
)

$ci = get-childitem $File
$fullName = $ci.FullName

$reader = new-object System.IO.StreamReader(New-Object IO.FileStream($fullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [IO.FileShare]::ReadWrite))
#start at the end of the file
$lastMaxOffset = $reader.BaseStream.Length - $InitialOffset

while ($true)
{
    #if the file size has not changed, idle
    if ($reader.BaseStream.Length -ge $lastMaxOffset) {
        #seek to the last max offset
        $reader.BaseStream.Seek($lastMaxOffset, [System.IO.SeekOrigin]::Begin) | out-null

        #read out of the file until the EOF
        $line = ""
        while (($line = $reader.ReadLine()) -ne $null) {
            write-output $line
        }

        #update the last max offset
        $lastMaxOffset = $reader.BaseStream.Position
    }

    if($Follow){
        Start-Sleep -m 100
    } else {
        break;
    }
}
Brian Reischl
quelle
0

Sehr einfach, macht aber das, was Sie brauchen, ohne Addon-Module oder PS-Versionsanforderungen:

while ($true) {Clear-Host; gc E:\test.txt | select -last 3; sleep 2 }

Jesse
quelle
4
Das ist bei großen Dateien brutal.
Pecos Bill
Meine Problemumgehung war: while($true) { Clear-Host; Get-Content <filename> -tail 40; sleep 1 }:)
NoLifeKing
0

Wahrscheinlich zu spät für eine Antwort, aber versuchen Sie es mit dieser

Get-Content <filename> -tail <number of items wanted>
EvosDeMercile
quelle
0

Es gab viele gültige Antworten, aber keine von ihnen hat die gleiche Syntax wie tail unter Linux . Die folgende Funktion kann aus Gründen der$Home\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 Persistenz in Ihrem gespeichert werden ( weitere Informationen finden Sie in der Dokumentation zu Powershell-Profilen ).

So können Sie anrufen ...

tail server.log
tail -n 5 server.log
tail -f server.log
tail -Follow -Lines 5 -Path server.log

Das kommt der Linux-Syntax ziemlich nahe.

function tail {
<#
    .SYNOPSIS
        Get the last n lines of a text file.
    .PARAMETER Follow
        output appended data as the file grows
    .PARAMETER Lines
        output the last N lines (default: 10)
    .PARAMETER Path
        path to the text file
    .INPUTS
        System.Int
        IO.FileInfo
    .OUTPUTS
        System.String
    .EXAMPLE
        PS> tail c:\server.log
    .EXAMPLE
        PS> tail -f -n 20 c:\server.log
#>
    [CmdletBinding()]
    [OutputType('System.String')]
    Param(
        [Alias("f")]
        [parameter(Mandatory=$false)]
        [switch]$Follow,

        [Alias("n")]
        [parameter(Mandatory=$false)]
        [Int]$Lines = 10,

        [parameter(Mandatory=$true, Position=5)]
        [ValidateNotNullOrEmpty()]
        [IO.FileInfo]$Path
    )
    if ($Follow)
    {
        Get-Content -Path $Path -Tail $Lines -Wait
    }
    else
    {
        Get-Content -Path $Path -Tail $Lines
    }
}
Patrick Stalph
quelle