Comando equivalente unix tail no Windows Powershell


349

Eu tenho que olhar para as últimas linhas de um arquivo grande (o tamanho típico é 500MB-2GB). Estou procurando um equivalente do comando Unix tailpara o Windows Powershell. Algumas alternativas disponíveis são:

http://tailforwin32.sourceforge.net/

e

Get-Content [nome do arquivo] | Select-Object -Último 10

Para mim, não é permitido usar a primeira alternativa, e a segunda alternativa é lenta. Alguém sabe de uma implementação eficiente de cauda para o PowerShell.


2
Como podemos saber se você poderá usar o que sugerimos, se não disser por que não pode usar a primeira alternativa?
Gabe

3
Algum motivo para você não poder usar o tailcomando fornecido em sourceforge.net/projects/unxutils/files/unxutils/current/… ?
Gabe

11
isso está em uma máquina de produção onde eu não tinha permissão para copiar copiar nenhum executável externo. Algumas políticas estranhas. :) Não posso evitar. Obrigado pelo link Unxutils.
Mutelogan


Não há necessidade de usar Select-Object: Get-Content [filename] -last 10e adicionar -tailpara -f
MortenB

Respostas:


492

Use o -waitparâmetro com Get-Content, que exibe as linhas à medida que são adicionadas ao arquivo. Esse recurso estava presente no PowerShell v1, mas por algum motivo não foi bem documentado na v2.

Aqui está um exemplo

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

Depois de executar isso, atualize e salve o arquivo e você verá as alterações no console.


16
Interessante. Eu teria pensado que todos os argumentos existentes também aparecem na ajuda, mas man gc -par waitme diz que não há parâmetro. Mas acho que isso não resolve o problema que o OP tem, já que eles pediram tail, tail -fe não uma implementação eficiente também. Como este também lê o arquivo completo antes de retornar as últimas linhas, isso é doloroso para os tamanhos de arquivo que eles esperam.
Joey

5
Para sua informação, é isso que a implementação Get-FileTail (alias tail) faz no PSCX. Se você estiver curioso, pode consultar o código-fonte: pscx.codeplex.com/SourceControl/changeset/view/78514#1358075
Keith Hill

7
@Joey -Wait é um parâmetro dinâmico que se aplica apenas ao provedor FileSystem. O GC pode ser usado em qualquer provedor que implemente essa API. A única maneira, além da documentação que eu sei descobrir, é usar (gcm Get-Content) .Parameters no caminho do provedor apropriado. Não use o alias "gc" porque os parâmetros dinâmicos não serão exibidos.
JasonMArcher

11
Eu sei que isso foi há um tempo atrás, mas isso exige que o processo de gravação no arquivo seja aberto, acrescentado e fechado antes que o Get-Content funcione. Se o processo de gravação nunca fechar o arquivo, ele não funcionará, o que não acontece com tail -f.
David Newcomb 31/03

15
Estranhamente, -Wait só me mostra novas linhas quando eu acesso um arquivo de log de alguma forma (como selecioná-lo no Windows Explorer). O Tail fornece atualizações à medida que novas linhas são gravadas no meu arquivo. Com -Wait, posso deixar uma janela do PowerShell aberta sem mostrar novas linhas enquanto o arquivo está sendo gravado. Se eu aparecer e clicar no arquivo no Windows Explorer, de repente o PowerShell "acorda" e recupera as linhas restantes. Isso é um inseto?
21712 JoshL

198

Para completar, mencionarei que o Powershell 3.0 agora tem um sinalizador -Tail em Get-Content

Get-Content ./log.log -Tail 10

obtém as últimas 10 linhas do arquivo

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

obtém as últimas 10 linhas do arquivo e aguarda mais

Além disso, para aqueles usuários * nix, nota que a maioria dos sistemas apelido gato para Get-Content, então Isto normalmente funciona

cat ./log.log -Tail 10

@LauraLiparulo de que maneira isso não funciona? Eu usei isso antes definitivamente.
George Mauer

4
Eu apenas usei e funcionou no local neste formatoGet-Content .\test.txt -Wait -Tail 1
Coops

@LauraLiparulo - Funciona para mim também:Get-Content -Path .\sync.log -Wait -Tail 10
elika kohen

No ISE, eu costumava usar while ($ true) / sleep e alternava para este, mas este também bloqueia o ISE inteiro e não pode executar scripts em outras guias. Devo apenas iniciar uma nova instância do ISE?
Teoman shipahi

@Teomanshipahi De que maneira o -Waitparâmetro não funcionou para você?
quer

116

No PowerShell versão 3.0, o cmdlet Get-Content possui um parâmetro -Tail que deve ajudar. Consulte a ajuda online da biblioteca technet para obter o conteúdo.



4
Nota para alguns - o PS 3.0 não está disponível no Windows XP e Vista.
tjmoore

11
Eu uso a técnica mencionada por Dan, mas eu a registro no meu $ PROFILE. Abra-o com o bloco de notas $ PROFILE. Em seguida, no documento de texto, crie uma nova função: function Tail ($ path) {Get-content -tail 15 -path $ path -wait} Dessa forma, você pode acessar a função sempre que iniciar o PowerShell.
Jake Nelson

Essa deve ser a resposta aceita. -A bandeira de espera mencionada na resposta atualmente aceita não funciona mais.
Abdullah Leghari

21

Eu usei algumas das respostas dadas aqui, mas apenas um aviso

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

irá consumir memória depois de um tempo. Um colega deixou essa "cauda" erguida no último dia e subiu para 800 MB. Não sei se a cauda do Unix se comporta da mesma maneira (mas duvido). Portanto, é bom usar para aplicativos de curto prazo, mas tenha cuidado com isso.


18

As extensões de comunidade do PowerShell (PSCX) fornecem o Get-FileTailcmdlet . Parece uma solução adequada para a tarefa. Nota: Eu não tentei com arquivos extremamente grandes, mas a descrição diz que domina eficientemente o conteúdo e foi projetada para arquivos de log grandes.

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.

11
Há um erro na versão atual que é corrigido em bits diários. Eu recomendaria pegar os bits mais recentes e compilá-los pelo menos até obtermos uma versão atualizada.
Keith Hill

7
A versão 2.0 demora muito tempo para mostrar as 10 últimas linhas de um arquivo CSV de 1GB, e diferentemente do Get-Content [filename] | Select-Object -Last 10que não pode ser abortado
Jader Dias

15

Apenas algumas adições às respostas anteriores. Existem aliases definidos para Get-Content, por exemplo, se você estiver acostumado ao UNIX cat, e há também typee gc. Então, ao invés de

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

você pode escrever

# 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

Usando o Powershell V2 e abaixo, o get-content lê o arquivo inteiro, por isso não me serviu de nada. O código a seguir funciona para o que eu precisava, embora haja provavelmente alguns problemas com as codificações de caracteres. Isso é efetivamente tail -f, mas pode ser facilmente modificado para obter os últimos x bytes, ou as últimas x linhas, se você quiser procurar para trás por quebras de linha.

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

Encontrei a maior parte do código para fazer isso aqui .


11
É verdade que o Get-Content com a opção -Tail lê o arquivo inteiro? Em arquivos grandes, parece bom para mim.
Govert

Eu acredito que depende da versão PS. Eu atualizei a resposta. Eu estava preso em um servidor sem a capacidade de instalar nada no momento, portanto o código acima foi útil.
hajamie

3

Peguei a solução @ hajamie e envolvi-a em um wrapper de script um pouco mais conveniente.

Eu adicionei uma opção para iniciar a partir de um deslocamento antes do final do arquivo, para que você possa usar a funcionalidade semelhante à cauda de ler uma certa quantidade no final do arquivo. Observe que o deslocamento está em bytes, não em linhas.

Há também uma opção para continuar esperando por mais conteúdo.

Exemplos (supondo que você salve isso como 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

E aqui está o próprio script ...

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

Muito básico, mas faz o que você precisa sem nenhum módulo adicional ou requisitos de versão PS:

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


4
Isso é brutal em arquivos grandes.
Pecos Bill,

Minha solução alternativa foi: while($true) { Clear-Host; Get-Content <filename> -tail 40; sleep 1 }:)
NoLifeKing

0

Provavelmente tarde demais para uma resposta, mas tente esta

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

0

Houve muitas respostas válidas, no entanto, nenhuma delas tem a mesma sintaxe que a tail no linux . A seguinte função pode ser armazenada no seu $Home\Documents\PowerShell\Microsoft.PowerShell_profile.ps1para persistência (consulte a documentação dos perfis do PowerShell para obter mais detalhes).

Isso permite que você ligue para ...

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

que chega bem perto da sintaxe do linux.

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
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.