Windows Powershell中类似Unix tail的命令是什么?

463
我需要查看一个大文件的最后几行(通常大小为500MB-2GB)。我正在寻找Windows PowerShell的等效Unix命令tail。有几个可用的替代方案,包括:

http://tailforwin32.sourceforge.net/

Get-Content [filename] | Select-Object -Last 10
对于我来说,不允许使用第一个替代方案,而第二个替代方案速度较慢。是否有人知道适用于PowerShell的高效实现tail的方法?

4
如果您不说为什么不能使用第一个选择,我们怎么知道您是否被允许使用我们建议的内容? - Gabe
3
你不能使用 http://sourceforge.net/projects/unxutils/files/unxutils/current/UnxUtils.zip/download 提供的 tail 命令,有什么原因吗?请翻译此内容。 - Gabe
1
这是在一个生产机器上,我不被允许复制任何外部可执行文件。一些奇怪的政策。 :) 无能为力。感谢Unxutils链接。 - mutelogan
https://devcentral.f5.com/blogs/us/unix-to-powershell-tail展示了纯PoSH实现的内容。 - Yevgeniy
4
无需使用 Select-Object:Get-Content [filename] -last 10并添加 -tail 参数即可实现 -f 的功能。 - MortenB
15个回答

628

使用-wait参数与Get-Content一起使用,它会显示添加到文件中的行。这个功能在PowerShell v1中就已经存在了,但是在v2中由于某些原因没有很好地记录。

下面是一个示例:

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

运行此代码后,更新并保存文件,您将在控制台上看到更改。


22
有趣。我本以为所有存在的参数都会在帮助文档中出现,但是 man gc -par wait 告诉我没有这个参数。但我认为这并不能解决原帖作者提出的问题,因为他们要求的是 tail 而不是 tail -f,而且还需要一个高效的实现。由于这个实现在返回最后几行前也要读取整个文件,所以对于他们期望的文件大小来说,这是很痛苦的。 - Joey
5
以下是“Get-FileTail”(别名为“tail”)在PSCX中的实现方式。如果您感兴趣,可以查看源代码:http://pscx.codeplex.com/SourceControl/changeset/view/78514#1358075 - Keith Hill
7
@Joey -Wait是一个仅适用于FileSystem提供程序的动态参数。GC可以用在任何实现该API的提供程序上。除了文档之外,我知道的发现这些参数的唯一方法是在适当的提供程序路径内使用(gcm Get-Content). Parameters。不要使用别名“gc”,因为动态参数将不会显示。 - JasonMArcher
11
我知道这是一段时间以前的事情,但是在使用Get-Content命令之前,需要先打开文件、附加内容并关闭文件。如果写入过程从未关闭文件,那么Get-Content命令将无法正常工作,而tail -f命令则不会出现这种情况。 - David Newcomb
16
奇怪的是,只有在某种方式下(比如在Windows资源管理器中选择它)访问日志文件时,“-Wait”命令才会向我显示新行。而“Tail”命令则会在新行写入文件时提供更新。使用“-Wait”命令,即使文件正在被写入中,我也可以愉快地保留打开的PowerShell窗口,不显示任何新行。如果我跳转并在Windows资源管理器中点击该文件,突然间PowerShell就“醒来”了,并且抓取了剩余的行。这是一个bug吗? - JoshL
显示剩余14条评论

301

为了完整起见,我会提到Powershell 3.0现在在Get-Content命令中有一个-Tail标志。

Get-Content ./log.log -Tail 10

获取文件的最后10行

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

获取文件的最后10行并等待更多内容。

此外,对于那些*nix用户,请注意大多数系统将cat别名为Get-Content,因此这通常可以工作。

cat ./log.log -Tail 10

@LauraLiparulo,这个方法有什么问题吗?我之前肯定用过。 - George Mauer
4
我刚刚使用了这个命令格式 Get-Content .\test.txt -Wait -Tail 1,并且它完美地起作用了。 - Paul C
在ISE上,我过去常使用while($true)/sleep,但现在改用了另外一个方法。但是这个新方法也会锁定整个ISE,并且不能在其他选项卡上运行脚本。我应该开启一个新的ISE实例吗? - Teoman shipahi
@Teomanshipahi 你是说-Wait参数在什么方面没有起作用? - George Mauer
@GeorgeMauer,我在概念上误解了ISE的工作原理。我以为不同的选项卡有自己的上下文,但如果一个脚本在一个选项卡上运行,我就不能在其他选项卡上运行其他脚本。我认为这是预期的行为。 - Teoman shipahi
显示剩余2条评论

123

1
下载链接在这里 - http://www.microsoft.com/zh-cn/download/details.aspx?id=34595。 - Gedrox
4
提示 - PS 3.0 不支持 Windows XP 和 Vista。 - tjmoore
1
我使用了Dan提到的技巧,但是我将它记录在我的$PROFILE中。用记事本打开$PROFILE。然后,在文本文档中创建一个新函数: function Tail ($path) { Get-content -tail 15 -path $path -wait } 这样,每次启动PowerShell时,您都可以访问该函数。 - Jake Nelson
这应该是被接受的答案。当前被接受的答案中提到的“等待标志”不再起作用了。 - Abdullah Leghari

31

我使用了这里提供的一些答案,但需要注意的是:

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

一段时间后,tail 命令会消耗大量内存。我的一个同事把 tail 命令运行了一整天,结果它占用了 800 MB 的内存。我不知道 Unix 的 tail 命令是否也会这样(但我觉得不会)。因此,在短期应用中可以使用它,但要小心。


19

PowerShell社区扩展(PSCX)提供了Get-FileTail cmdlet。它看起来是这个任务的合适解决方案。注意:我没有尝试过它用于非常大的文件,但描述说它可以有效地追踪内容并且专为大型日志文件设计。

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
当前版本中存在一个错误,这个错误在每日版本中已经修复。我建议获取最新的版本并编译它们,至少在我们发布更新版本之前这样做。 - Keith Hill
7
2.0版本花费很长时间才能显示1GB CSV文件的最后10行,与“Get-Content [filename] | Select-Object -Last 10”不同,它无法中止。 - Jader Dias

19

可能已经太晚回答了,但是尝试这个

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

这些都没有关注选项,这是使用“tail”命令的99%用例。 - Andries
1
@Andries,你看到Ravikanth的回答了吗?你可以使用“-Wait”来跟踪。 - Jason S
1
添加了@JasonS建议的标志。 - EvosDeMercile

15

仅是之前答案的一些补充。Get-Content 命令有别名,例如如果您习惯于 UNIX 系统,您可能会喜欢使用 cat 命令,还有 typegc 命令。因此,代替使用

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

你可以写

# 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

4

我采用了@hajamie的解决方案,并将其包装成了一个更加便捷的脚本包装器。

我添加了一个选项,可以从文件结尾之前的偏移量开始读取,因此您可以使用类似tail的功能从文件末尾读取一定量的内容。请注意,偏移量是按字节计算的,而不是按行计算的。

还有一个选项可以继续等待更多内容。

示例(假设您将其保存为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

以下是脚本本身...

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

4

我有一个关于多文件相关的实用技巧。

使用PowerShell 5.2(Win7和Win10)可以很容易地跟踪单个日志文件(类似于Linux中的“tail -f”),只需使用"Get-Content MyFile -Tail 1 -Wait"命令即可。但是,同时观察多个日志文件似乎比较复杂。然而,使用PowerShell 7.x+中的"Foreach-Object -Parallel"命令可以轻松解决这个问题。这将并行执行多个 'Get-Content' 命令。例如:

Get-ChildItem C:\logs\*.log | Foreach-Object -Parallel { Get-Content $_ -Tail 1 -Wait }

4

使用Powershell V2及以下版本,get-content命令会读取整个文件,因此对我没有用处。下面的代码可以满足我的需求,但字符编码可能存在问题。这实际上是tail -f命令,但如果您想向后搜索换行符,则可以轻松地修改为获取最后x个字节或最后x行。

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

我在这里找到了大部分实现此功能的代码(链接)

1
Get-Content 命令中的 -Tail 选项是否真的会读取整个文件?在大文件上似乎对我来说没问题。 - Govert
我相信这取决于PS的版本。我已经更新了答案。当时我被困在一个无法安装任何东西的服务器上,所以上面的代码非常有用。 - hajamie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接