Comando equivalente de cola de Unix en Windows Powershell

349

Tengo que mirar las últimas líneas de un archivo grande (el tamaño típico es de 500 MB a 2 GB). Estoy buscando un equivalente del comando Unix tailpara Windows Powershell. Algunas alternativas disponibles en son,

http://tailforwin32.sourceforge.net/

y

Obtener contenido [nombre de archivo] | Seleccionar-Objeto -Los últimos 10

Para mí, no está permitido usar la primera alternativa, y la segunda alternativa es lenta. ¿Alguien sabe de una implementación eficiente de tail para PowerShell?

mutelogan
fuente
2
¿Cómo podemos saber si se le permitirá usar lo que sugerimos si no dice por qué no se le permite usar la primera alternativa?
Gabe
3
¿Alguna razón por la que no puede usar el tailcomando proporcionado en sourceforge.net/projects/unxutils/files/unxutils/current/… ?
Gabe
1
esto está en una máquina de producción donde no se me permitió copiar copiar ningún ejecutable externo. Algunas políticas raras. :) No puedo evitarlo. Gracias por el enlace Unxutils.
mutelogan
https://devcentral.f5.com/blogs/us/unix-to-powershell-tail demuestra la implementación pura de PoSH de esto.
Yevgeniy
No es necesario usar Select-Object: Get-Content [filename] -last 10y agregar -tailpara -f
MortenB

Respuestas:

492

Use el -waitparámetro con Get-Content, que muestra líneas a medida que se agregan al archivo. Esta característica estaba presente en PowerShell v1, pero por alguna razón no está bien documentada en v2.

Aquí hay un ejemplo

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

Una vez que ejecute esto, actualice y guarde el archivo y verá los cambios en la consola.

ravikanth
fuente
16
Interesante. Pensé que todos los argumentos que existen también aparecen en la ayuda, pero man gc -par waitme dice que no hay ningún parámetro. Pero creo que esto no resuelve el problema que tiene el OP, ya que lo pidieron tail, tail -fy tampoco una implementación eficiente. Como este también lee el archivo completo antes de devolver las últimas líneas, esto es doloroso para los tamaños de archivo que esperan.
Joey
55
Para su información, esto es lo que hace la implementación de Get-FileTail (cola de alias) en PSCX. Si tiene curiosidad, puede consultar el código fuente: pscx.codeplex.com/SourceControl/changeset/view/78514#1358075
Keith Hill el
77
@Joey -Wait es un parámetro dinámico que solo se aplica al proveedor de FileSystem. GC se puede usar en cualquier proveedor que implemente esa API. La única forma, además de la documentación que conozco para descubrirlos, es usar (gcm Get-Content). Parámetros desde la ruta del proveedor correspondiente. No use el alias "gc" porque los parámetros dinámicos no se mostrarán.
JasonMArcher
11
Sé que fue hace un tiempo, pero esto requiere el proceso de escritura en el archivo para abrir, agregar y cerrar antes de que Get-Content funcione. Si el proceso de escritura nunca cierra el archivo, entonces no funcionará, que no es el caso con tail -f.
David Newcomb
15
Curiosamente, -Wait solo me muestra nuevas líneas cuando accedo a un archivo de registro de alguna manera (como seleccionarlo en el Explorador de Windows). Tail proporciona actualizaciones a medida que se escriben nuevas líneas en mi archivo. Con -Wait, puedo dejar abierta una ventana de PowerShell que no muestra nuevas líneas mientras se escribe el archivo. Si aparece y hago clic en el archivo en el Explorador de Windows, de repente PowerShell "se despierta" y recupera las líneas restantes. ¿Es esto un error?
JoshL
198

Para completar, mencionaré que Powershell 3.0 ahora tiene un indicador de cola en Get-Content

Get-Content ./log.log -Tail 10

obtiene las últimas 10 líneas del archivo

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

obtiene las últimas 10 líneas del archivo y espera más

Además, para aquellos usuarios * nix, nota que la mayoría de los sistemas de alias gato a Get-contenido, por lo que esto funciona normalmente

cat ./log.log -Tail 10
George Mauer
fuente
@LauraLiparulo, ¿de qué manera esto no funciona? Lo he usado antes definitivamente.
George Mauer
44
Lo acabo de usar y funcionó en este formatoGet-Content .\test.txt -Wait -Tail 1
Coops
@LauraLiparulo - También funciona para mí:Get-Content -Path .\sync.log -Wait -Tail 10
elika kohen
En ISE, solía usar while ($ true) / sleep y cambié a este, pero este también bloquea todo el ISE y no puede ejecutar scripts en otras pestañas. ¿Debo comenzar una nueva instancia de ISE?
Teoman shipahi
@Teomanshipahi ¿De qué manera el -Waitparámetro no funcionó para usted?
George Mauer
116

A partir de PowerShell versión 3.0, el cmdlet Get-Content tiene un parámetro -Tail que debería ayudar. Consulte la ayuda en línea de la biblioteca de Technet para obtener contenido.

Dan Blanchard
fuente
1
Enlace para descargar aquí: microsoft.com/en-us/download/details.aspx?id=34595 .
Gedrox
44
Nota para algunos: PS 3.0 no está disponible para Windows XP y Vista.
tjmoore
1
Utilizo la técnica mencionada por Dan pero la registro en mi $ PROFILE. Ábralo con el bloc de notas $ PROFILE. Luego, en el documento de texto, cree una nueva función: function Tail ($ path) {Get-content -tail 15 -path $ path -wait} De esta manera puede acceder a la función cada vez que inicie PowerShell.
Jake Nelson el
Esta debería ser la respuesta aceptada. -La bandera de espera mencionada en la respuesta actualmente aceptada ya no funciona.
Abdullah Leghari
21

Utilicé algunas de las respuestas dadas aquí, pero solo un aviso

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

masticará la memoria después de un tiempo. Un colega dejó tal "cola" en el último día y subió a 800 MB. No sé si la cola de Unix se comporta de la misma manera (pero lo dudo). Por lo tanto, está bien usarlo para aplicaciones a corto plazo, pero tenga cuidado con eso.

John Lockwood
fuente
18

PowerShell Community Extensions (PSCX) proporciona el Get-FileTailcmdlet . Parece una solución adecuada para la tarea. Nota: No lo probé con archivos extremadamente grandes, pero la descripción dice que ajusta eficientemente los contenidos y está diseñado para archivos de registro 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.
Roman Kuzmin
fuente
1
Hay un error en la versión actual que se corrige en bits diarios. Recomendaría tomar los últimos bits y compilarlos al menos hasta que se publique una versión actualizada.
Keith Hill
77
La versión 2.0 lleva años para mostrar las 10 últimas líneas de un archivo csv de 1GB, y de manera diferente Get-Content [filename] | Select-Object -Last 10no puede ser abortado
Jader Dias
15

Solo algunas adiciones a respuestas anteriores. Hay alias definidos para Get-Content, por ejemplo, si está acostumbrado a UNIX que le gustaría cat, y también hay typey gc. Entonces en lugar de

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

puedes escribir

# 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
fuente
3

Usando Powershell V2 y versiones posteriores, get-content lee el archivo completo, por lo que no me sirvió de nada. El siguiente código funciona para lo que necesitaba, aunque es probable que haya algunos problemas con las codificaciones de caracteres. Esto es efectivamente tail -f, pero podría modificarse fácilmente para obtener los últimos x bytes, o las últimas x líneas si desea buscar saltos de línea hacia atrás.

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

Encontré la mayor parte del código para hacer esto aquí .

hajamie
fuente
1
¿Es cierto que Get-Content con la opción -Tail lee todo el archivo? En archivos grandes me parece bien.
Govert
Creo que depende de la versión de PS. He actualizado la respuesta. Estaba atascado en un servidor sin la capacidad de instalar nada en ese momento, por lo que el código anterior fue útil.
hajamie
3

Tomé la solución de @hajamie y la envolví en un contenedor de script un poco más conveniente.

Agregué una opción para comenzar desde un desplazamiento antes del final del archivo, para que pueda usar la funcionalidad de cola de leer una cierta cantidad desde el final del archivo. Tenga en cuenta que el desplazamiento está en bytes, no en líneas.

También hay una opción para seguir esperando más contenido.

Ejemplos (suponiendo que guarde esto 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

Y aquí está el guión en sí ...

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
fuente
0

Muy básico, pero hace lo que necesita sin ningún módulo adicional o requisitos de versión PS:

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

Jesse
fuente
44
Eso es brutal en archivos grandes.
Pecos Bill
Mi solución fue: while($true) { Clear-Host; Get-Content <filename> -tail 40; sleep 1 }:)
NoLifeKing
0

Probablemente sea demasiado tarde para una respuesta pero, prueba esta

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

Ha habido muchas respuestas válidas, sin embargo, ninguna de ellas tiene la misma sintaxis que tail en Linux . La siguiente función se puede almacenar en su $Home\Documents\PowerShell\Microsoft.PowerShell_profile.ps1persistencia (consulte la documentación de los perfiles de PowerShell para obtener más detalles).

Esto le permite llamar ...

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

que se acerca bastante a la sintaxis de 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
    }
}
Patrick Stalph
fuente