获取 Windows 中大文件的最后 n 行或字节(如 Unix 的尾巴)。避免耗时的选择

Get last n lines or bytes of a huge file in Windows (like Unix's tail). Avoid time consuming options

我需要在 Windows 7 中检索最后 n 行大文件 (1-4 Gb)。 由于公司限制,我不能 运行 任何不是 built-in 的命令。 问题是我找到的所有解决方案似乎都在读取整个文件,因此它们非常慢。

能不能快点完成?

备注:

  1. 我设法快速获得了前 n 行。
  2. 如果我得到最后n个字节就可以了。 (我用这个 作为前 n 个字节)。

此处 Unix tail equivalent command in Windows Powershell 的解决方案无效。 使用 -wait 并不能使它变快。我没有 -tail(我不知道它是否会很快工作)。

PS:headtail的相关问题比较多,但没有集中在速度问题上。因此,那里有用或已接受的答案在这里可能没有用。例如,

Windows equivalent of the 'tail' command

CMD.EXE batch script to display last 10 lines from a txt file

Extract N lines from file using single windows command

https://serverfault.com/questions/490841/how-to-display-the-first-n-lines-of-a-command-output-in-windows-the-equivalent

powershell to get the first x MB of a file

https://superuser.com/questions/859870/windows-equivalent-of-the-head-c-command

如果您有 PowerShell 3 或更高版本,您可以使用 Get-Content-Tail 参数来获取最后 n 行。

Get-content -tail 5 PATH_TO_FILE;

在我本地 SSD 上的 34MB 文本文件上,返回时间为 1 毫秒,而 get-content |select -last 5

为 8.5 秒

这个怎么样(读取最后 8 个字节用于演示):

$fpath = "C:GBfile.dat"
$fs = [IO.File]::OpenRead($fpath)
$fs.Seek(-8, 'End') | Out-Null
for ($i = 0; $i -lt 8; $i++)
{
    $fs.ReadByte()
}

更新。要将字节解释为字符串(但一定要 select 正确编码 - 这里使用 UTF8):

$N = 8
$fpath = "C:GBfile.dat"
$fs = [IO.File]::OpenRead($fpath)
$fs.Seek(-$N, [System.IO.SeekOrigin]::End) | Out-Null
$buffer = new-object Byte[] $N
$fs.Read($buffer, 0, $N) | Out-Null
$fs.Close()
[System.Text.Encoding]::UTF8.GetString($buffer)

更新 2。要读取最后 M 行,我们将按部分读取文件,直到结果中有 多于 M 个换行字符序列:

$M = 3
$fpath = "C:GBfile.dat"

$result = ""
$seq = "`r`n"
$buffer_size = 10
$buffer = new-object Byte[] $buffer_size

$fs = [IO.File]::OpenRead($fpath)
while (([regex]::Matches($result, $seq)).Count -lt $M)
{
    $fs.Seek(-($result.Length + $buffer_size), [System.IO.SeekOrigin]::End) | Out-Null
    $fs.Read($buffer, 0, $buffer_size) | Out-Null
    $result = [System.Text.Encoding]::UTF8.GetString($buffer) + $result
}
$fs.Close()

($result -split $seq) | Select -Last $M

尝试使用更大的 $buffer_size - 理想情况下,这等于预期的平均行长度,以减少磁盘操作。还要注意 $seq - 这可能是 \r\n 或只是 \n。 这是非常脏的代码,没有任何错误处理和优化。

使用 ,它解决了速度问题,通过一些谷歌搜索,我最终使用了这个脚本

$fpath = $Args[1]
$fs = [IO.File]::OpenRead($fpath)
$fs.Seek(-$Args[0], 'End') | Out-Null
$mystr = ''
for ($i = 0; $i -lt $Args[0]; $i++)
{
    $mystr = ($mystr) + ([char[]]($fs.ReadByte()))
}
$fs.Close()
Write-Host $mystr

我从包含

的批处理文件中调用
@PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '.\myscript.ps1' %1 %2"

(感谢 How to run a PowerShell script from a batch file)。

这不是答案,而是对 sancho.s 答案的回复。

当您想从批处理文件中使用小型 PowerShell 脚本时,我建议您使用下面的方法,该方法更简单并且可以将所有代码保存在同一个批处理文件中:

@PowerShell  ^
   $fpath = %2;  ^
   $fs = [IO.File]::OpenRead($fpath);  ^
   $fs.Seek(-%1, 'End') ^| Out-Null;  ^
   $mystr = '';  ^
   for ($i = 0; $i -lt %1; $i++)  ^
   {  ^
      $mystr = ($mystr) + ([char[]]($fs.ReadByte()));  ^
   }  ^
   Write-Host $mystr
%End PowerShell%

当文件已经打开时,最好使用

Get-Content $fpath -tail 10

因为“使用“1”个参数调用“OpenRead”时出现异常:“进程无法访问文件...”

获取文件的最后 n 个字节:

set file="C:\Covid.mp4"
set n=7

copy /b %file% tmp
for %i in (tmp) do set /a m=%~zi-%n%
FSUTIL file seteof tmp %m%
fsutil file createnew temp 1
FSUTIL file seteof temp %n%
type temp >> tmp
fc /b tmp %file% | more +1 > temp

REM problem parsing file with byte offsets in hex from fc, to be converted to decimal offsets before output
type nul > tmp
for /f "tokens=1-3 delims=: " %i in (temp) do set /a 0x%i >> tmp & set /p=": " <nul>> tmp & echo %j %k >> tmp

set /a n=%m%+%n%-1

REM output
type nul > temp
for /l %j in (%m%,1,%n%) do (find "%j: "<  tmp || echo doh: la 00)>> temp
(for /f "tokens=3" %i in (temp) do set /p=%i <nul) & del tmp & del temp

在 Win 10 cmd Surface Laptop 1 上测试
结果:10 秒内处理了 1.43 GB 的文件