Komenda odpowiednika ogona uniksowego w Windows Powershell


349

Muszę spojrzeć na kilka ostatnich wierszy dużego pliku (typowy rozmiar to 500 MB-2 GB). Szukam odpowiednika polecenia Unix taildla Windows Powershell. Kilka dostępnych alternatyw to:

http://tailforwin32.sourceforge.net/

i

Get-Content [nazwa pliku] | Wybierz obiekt - ostatni 10

Dla mnie nie wolno używać pierwszej alternatywy, a druga alternatywa jest powolna. Czy ktoś wie o skutecznej implementacji taila dla PowerShell.


2
Skąd możemy wiedzieć, czy będziesz mógł korzystać z tego, co sugerujemy, jeśli nie powiesz, dlaczego nie możesz użyć pierwszej alternatywy?
Gabe,

3
Z jakiegokolwiek powodu nie możesz użyć tailpolecenia podanego w sourceforge.net/projects/unxutils/files/unxutils/current/… ?
Gabe,

1
to jest na maszynie produkcyjnej, gdzie nie wolno mi było kopiować żadnych zewnętrznych plików wykonywalnych. Niektóre dziwne zasady. :) Nic na to nie poradzę. Dzięki za link Unxutils.
mutelogan,

https://devcentral.f5.com/blogs/us/unix-to-powershell-tail demonstruje czystą implementację PoSH tego.
Jewgienij

Nie trzeba używać Select-Object: Get-Content [filename] -last 10i dodać -taildla -f
MortenB

Odpowiedzi:


492

Użyj -waitparametru z Get-Content, który wyświetla linie w miarę ich dodawania do pliku. Ta funkcja była obecna w PowerShell v1, ale z jakiegoś powodu nie została dobrze udokumentowana w v2.

Oto przykład

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

Po uruchomieniu zaktualizuj i zapisz plik, a zobaczysz zmiany na konsoli.


16
Ciekawy. Myślałbym, że wszystkie istniejące argumenty pojawiają się również w pomocy, ale man gc -par waitmówi mi, że nie ma parametru. Myślę jednak, że to nie rozwiązuje problemu PO, ponieważ o to prosili tail, tail -fa także skutecznego wdrożenia. Ponieważ ten odczytuje również cały plik przed zwróceniem ostatnich wierszy, jest to bolesne dla oczekiwanych rozmiarów plików.
Joey,

5
Do Twojej dyspozycji jest to implementacja Get-FileTail (alias tail) w PSCX. Jeśli jesteś ciekawy, możesz zajrzeć do kodu źródłowego: pscx.codeplex.com/SourceControl/changeset/view/78514#1358075
Keith Hill,

7
@Joey -Wait to parametr dynamiczny, który dotyczy tylko dostawcy FileSystem. Z GC można korzystać na dowolnym dostawcy, który implementuje ten interfejs API. Jedynym sposobem poza dokumentacją, którą znam, aby je odkryć, jest użycie (gcm Get-Content) .Parametry z odpowiedniej ścieżki dostawcy. Nie używaj aliasu „gc”, ponieważ parametry dynamiczne nie pojawią się.
JasonMArcher,

11
Wiem, że to było dawno temu, ale wymaga to procesu zapisu do pliku, aby go otworzyć, dołączyć, zamknąć, zanim Get-Content zadziała. Jeśli proces zapisu nigdy nie zamyka pliku, nie będzie on działał, co nie ma miejsca w przypadku tail -f.
David Newcomb

15
Dziwnie, -Czekaj pokazuje mi nowe linie tylko wtedy, gdy uzyskuję dostęp do pliku dziennika w jakiś sposób (na przykład wybierając go w Eksploratorze Windows). Tail zapewnia aktualizacje, gdy nowe wiersze są zapisywane w moim pliku. Za pomocą opcji -Wait mogę pozostawić otwarte okno programu PowerShell bez pokazywania nowych wierszy podczas zapisywania pliku. Jeśli następnie wyskoczę i kliknę plik w Eksploratorze Windows, nagle program PowerShell „budzi się” i przechwytuje pozostałe linie. Czy to błąd?
JoshL

197

Dla kompletności wspomnę, że Powershell 3.0 ma teraz flagę -Tail na Get-Content

Get-Content ./log.log -Tail 10

pobiera ostatnie 10 linii pliku

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

pobiera ostatnie 10 wierszy pliku i czeka na więcej

Ponadto, dla tych użytkowników * nix, zauważ, że większość systemów aliasów cat na Get-Content, więc to zwykle działa

cat ./log.log -Tail 10

@LauraLiparulo w jaki sposób to nie działa? Zdecydowanie z niego korzystałem.
George Mauer,

4
Właśnie go użyłem i zadziałało w tym formacieGet-Content .\test.txt -Wait -Tail 1
Coops

@LauraLiparulo - Działa również dla mnie:Get-Content -Path .\sync.log -Wait -Tail 10
elika kohen

Na ISE używałem while ($ true) / sleep i przełączałem się na ten, ale ten blokuje również cały ISE i nie może uruchamiać skryptów na innych kartach. Czy powinienem po prostu rozpocząć nową instancję ISE?
Teoman shipahi

@Teomanshipahi W jaki sposób -Waitparametr nie działał dla Ciebie?
George Mauer,

116

Od wersji PowerShell 3.0 cmdlet Get-Content ma parametr -Tail , który powinien pomóc. Zobacz pomoc online biblioteki technet dotyczącą Get-Content.



4
Uwaga dla niektórych - PS 3.0 jest niedostępny dla Windows XP i Vista.
tjmoore

1
Używam techniki wymienionej przez Dana, ale zapisuję ją w moim profilu $ PROFIL. Otwórz go za pomocą notatnika $ PROFIL. Następnie w dokumencie tekstowym utwórz nową funkcję: function Tail ($ path) {Get-content -tail 15 -path $ path -wait} W ten sposób możesz uzyskać dostęp do funkcji przy każdym uruchomieniu programu PowerShell.
Jake Nelson

To powinna być zaakceptowana odpowiedź. - Flaga czekania wymieniona w aktualnie akceptowanej odpowiedzi już nie działa.
Abdullah Leghari,

21

Użyłem niektórych z podanych tutaj odpowiedzi, ale tylko na pierwszy rzut oka

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

po chwili przeżuje pamięć. Kolega zostawił taki „ogon” w ciągu ostatniego dnia i wzrósł do 800 MB. Nie wiem, czy ogon uniksowy zachowuje się w ten sam sposób (ale wątpię w to). Dlatego dobrze jest używać do krótkoterminowych aplikacji, ale bądź ostrożny.


18

Rozszerzenia społeczności programu PowerShell (PSCX) zapewniają polecenie Get-FileTailcmdlet . Wygląda na odpowiednie rozwiązanie do tego zadania. Uwaga: nie próbowałem tego z bardzo dużymi plikami, ale opis mówi, że skutecznie ogona zawartości i jest przeznaczony do dużych plików dziennika.

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.

1
W obecnej wersji jest błąd, który jest naprawiany w codziennych bitach. Poleciłbym pobrać najnowsze bity i skompilować je przynajmniej do czasu wydania zaktualizowanej wersji.
Keith Hill,

7
Wersja 2.0 potrzebuje wieków, aby wyświetlić 10 ostatnich wierszy pliku csv o pojemności 1 GB i inaczej Get-Content [filename] | Select-Object -Last 10nie można go przerwać
Jader Dias

15

Kilka dodatków do poprzednich odpowiedzi. Istnieją zdefiniowane aliasy dla Get-Content, na przykład jeśli jesteś przyzwyczajony do systemu UNIX, który może ci się spodobać cat, a także istnieją typei gc. Więc zamiast

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

Możesz pisać

# 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

3

Korzystając z Powershell V2 i niższych, get-content czyta cały plik, więc nie przydało mi się to. Poniższy kod działa w przypadku tego, czego potrzebowałem, chociaż prawdopodobnie występują pewne problemy z kodowaniem znaków. Jest to efektywnie tail -f, ale można go łatwo zmodyfikować, aby uzyskać ostatnie x bajtów lub ostatnie x linii, jeśli chcesz szukać podziałów linii wstecz.

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

Znalazłem większość kodu, aby to zrobić tutaj .


1
Czy to prawda, że ​​Get-Content z opcją -Tail czyta cały plik? W przypadku dużych plików wydaje mi się to w porządku.
Govert,

Myślę, że to zależy od wersji PS. Zaktualizowałem odpowiedź. Utknąłem na serwerze bez możliwości instalowania czegokolwiek w tym czasie, więc powyższy kod był przydatny.
hajamie 17.04.15

3

Wziąłem rozwiązanie @ hajamie i zapakowałem je w nieco wygodniejsze opakowanie skryptu.

Dodałem opcję, aby zacząć od przesunięcia przed końcem pliku, abyś mógł skorzystać z podobnej do ogona funkcji odczytu pewnej ilości z końca pliku. Uwaga: przesunięcie jest w bajtach, a nie w liniach.

Istnieje również opcja kontynuowania oczekiwania na więcej treści.

Przykłady (zakładając, że zapisujesz to jako 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

A oto sam skrypt ...

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


0

Bardzo prosty, ale robi to, czego potrzebujesz, bez żadnych modułów dodatkowych lub wymagań wersji PS:

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


4
To brutalne w przypadku dużych plików.
Pecos Bill

Moje obejście: while($true) { Clear-Host; Get-Content <filename> -tail 40; sleep 1 }:)
NoLifeKing

0

Prawdopodobnie za późno na odpowiedź, ale spróbuj tego

Get-Content <filename> -tail <number of items wanted>

0

Było wiele poprawnych odpowiedzi, jednak żadna z nich nie ma takiej samej składni jak tail w systemie Linux . Poniższa funkcja może być przechowywana w twoim serwerze w $Home\Documents\PowerShell\Microsoft.PowerShell_profile.ps1celu zachowania trwałości ( więcej informacji znajduje się w dokumentacji profili programu PowerShell ).

To pozwala dzwonić ...

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

co jest bardzo zbliżone do składni linuksowej.

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
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.