获取 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 的命令。
问题是我找到的所有解决方案似乎都在读取整个文件,因此它们非常慢。
能不能快点完成?
备注:
- 我设法快速获得了前 n 行。
- 如果我得到最后n个字节就可以了。 (我用这个 作为前 n 个字节)。
此处 Unix tail equivalent command in Windows Powershell 的解决方案无效。
使用 -wait
并不能使它变快。我没有 -tail
(我不知道它是否会很快工作)。
PS:head
和tail
的相关问题比较多,但没有集中在速度问题上。因此,那里有用或已接受的答案在这里可能没有用。例如,
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
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 的文件
我需要在 Windows 7 中检索最后 n 行大文件 (1-4 Gb)。 由于公司限制,我不能 运行 任何不是 built-in 的命令。 问题是我找到的所有解决方案似乎都在读取整个文件,因此它们非常慢。
能不能快点完成?
备注:
- 我设法快速获得了前 n 行。
- 如果我得到最后n个字节就可以了。 (我用这个 作为前 n 个字节)。
此处 Unix tail equivalent command in Windows Powershell 的解决方案无效。
使用 -wait
并不能使它变快。我没有 -tail
(我不知道它是否会很快工作)。
PS:head
和tail
的相关问题比较多,但没有集中在速度问题上。因此,那里有用或已接受的答案在这里可能没有用。例如,
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
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 个字节用于演示):
$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 的文件