I have to look at the last few lines of a large file (typical size is 500MB-2GB). I am looking for a equivalent of Unix command tail for Windows Powershell. A few alternatives available on are,
and
Get-Content [filename] | Select-Object -Last 10
For me, it is not allowed to use the first alternative, and the second alternative is slow. Does anyone know of an efficient implementation of tail for PowerShell.
514 Answers
Use the -wait parameter with Get-Content, which displays lines as they are added to the file. This feature was present in PowerShell v1, but for some reason not documented well in v2.
Here is an example
Get-Content -Path "C:\scripts\test.txt" -Wait Once you run this, update and save the file and you will see the changes on the console.
19For completeness I'll mention that Powershell 3.0 now has a -Tail flag on Get-Content
Get-Content ./log.log -Tail 10 gets the last 10 lines of the file
Get-Content ./log.log -Wait -Tail 10 gets the last 10 lines of the file and waits for more
Also, for those *nix users, note that most systems alias cat to Get-Content, so this usually works
cat ./log.log -Tail 10 6As of PowerShell version 3.0, the Get-Content cmdlet has a -Tail parameter that should help. See the technet library online help for Get-Content.
4I used some of the answers given here but just a heads up that
Get-Content -Path Yourfile.log -Tail 30 -Wait will chew up memory after awhile. A colleague left such a "tail" up over the last day and it went up to 800 MB. I don't know if Unix tail behaves the same way (but I doubt it). So it's fine to use for short term applications, but be careful with it.
PowerShell Community Extensions (PSCX) provides the Get-FileTail cmdlet. It looks like a suitable solution for the task. Note: I did not try it with extremely large files but the description says it efficiently tails the contents and it is designed for large log files.
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. 2Just some additions to previous answers. There are aliases defined for Get-Content, for example if you are used to UNIX you might like cat, and there are also type and gc. So instead of
Get-Content -Path <Path> -Wait -Tail 10 you can write
# 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 Probably too late for an answere but, try this one
Get-Content <filename> -tail <number of items wanted> -wait 3Using Powershell V2 and below, get-content reads the entire file, so it was of no use to me. The following code works for what I needed, though there are likely some issues with character encodings. This is effectively tail -f, but it could be easily modified to get the last x bytes, or last x lines if you want to search backwards for line breaks.
$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 } I found most of the code to do this here.
2I took @hajamie's solution and wrapped it up into a slightly more convenient script wrapper.
I added an option to start from an offset before the end of the file, so you can use the tail-like functionality of reading a certain amount from the end of the file. Note the offset is in bytes, not lines.
There's also an option to continue waiting for more content.
Examples (assuming you save this as 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 And here is the script itself...
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; } } 0try Windows Server 2003 Resource Kit Tools
it contains a tail.exe which can be run on Windows system.
There have been many valid answers, however, none of them has the same syntax as tail in linux. The following function can be stored in your $Home\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 for persistency (see powershell profiles documentation for more details).
This allows you to call...
tail server.log tail -n 5 server.log tail -f server.log tail -Follow -Lines 5 -Path server.log which comes quite close to the linux syntax.
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 } } I have a useful tip on this subject concerning multiple files.
Following a single log file (like 'tail -f' in Linux) with PowerShell 5.2 (Win7 and Win10) is easy (just use "Get-Content MyFile -Tail 1 -Wait"). However, watching MULTIPLE log files at once seems complicated. With PowerShell 7.x+ however, I've found an easy way by using "Foreach-Object -Parrallel". This performs multiple 'Get-Content' commands concurrently. For example:
Get-ChildItem C:\logs\*.log | Foreach-Object -Parallel { Get-Content $_ -Tail 1 -Wait } Very basic, but does what you need without any addon modules or PS version requirements:
while ($true) {Clear-Host; gc E:\test.txt | select -last 3; sleep 2 }
It is possible to download all of the UNIX commands compiled for Windows from this GitHub repository:
1