2016-04-08 46 views
7

我需要檢索Windows 7中最後n行的大文件(1-4 Gb)。 由於公司的限制,我無法運行任何不是內置的命令。 問題是我找到的所有解決方案似乎都讀取整個文件,所以它們非常慢。獲取Windows中一個巨大文件的最後n行或字節(如Unix的尾部)。避免耗時選項

這可以快速完成嗎?

注:

  1. 我設法得到前n行,速度快。
  2. 如果我得到最後的n個字節就可以了。 (我用這個https://stackoverflow.com/a/18936628/2707864爲前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

+0

批處理文件是一個不好的選擇,因爲正確處理二進制文件是非常困難甚至幾乎不可能的(我想你是在談論諸如你想提取一定數量的_bytes_而不是字符或行);所以我肯定會去PS ... – aschipfl

回答

8

這個怎麼樣(讀取最後8個字節用於演示):

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

UPDATE。爲了解釋字節字符串(但一定要選擇正確的編碼 - 在這裏使用UTF8):

$N = 8 
$fpath = "C:\10GBfile.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 = 3 
$fpath = "C:\10GBfile.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。 這是非常髒的代碼,沒有任何錯誤處理和優化。

+0

這實際上工作得很快,但它爲每個字節輸出十進制代碼。我的意思是獲得相應的字符串。 –

+0

已更新,請檢查。只是注意到我在第一個示例中忘記了'$ fs.Close()',但我希望對於這個概念驗證代碼沒有那麼重要。祝你好運! –

+0

謝謝!我正在編寫自己的代碼,併發布了一個可行的答案。我通常不會編碼PS,所以它可能是最基本的。 –

-2

嘗試

Get-Content <file name> | Select-Object -Last 3 # it prints last 3 lines of the file 
+3

這將讀取整個文件,這將在4GB文件上變慢。 – alroc

+0

與alroc相同的評論。 –

17

如果您有PowerShell的3或更高版本,可以使用-Tail參數Get-Content得到最後n線。

Get-content -tail 5 PATH_TO_FILE; 

在我的本地SSD一個34MB的文本文件,這回在1毫秒與8.5秒爲get-content |select -last 5

+0

我沒有'-Tail'。 –

+0

然後將您的環境升級到最新版本的PowerShell。除非您需要保留一些奇怪的兼容性問題,否則沒有理由不升級至v3,最好是4或5(無論您的系統支持的最高級別是什麼)。 – alroc

+0

由於相同的公司限制,我無法運行任何不是內置的命令,因此我無法升級。我得到他們給我的東西。 –

1

隨着the awesome answer by Aziz Kabyshev,解決了速度的問題,並與一些google搜索,我結束了使用這個腳本

$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)。

+0

字節到字符總是依賴於編碼,不要忘記它 –

+0

@AzizKabyshev - 那是真實的。對於我所知道的文件,這是可以的。 –

1

這不是一個答案,而是一個很大的評論,作爲答覆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% 
+0

這對我來說非常有用。警告:執行此操作的方式是使用'myscript.bat nbytes'myfile''。使用帶有單引號的文件名是強制性的。沒有引號或雙引號不起作用,與執行調用ps1腳本的批處理文件相反。 –

+0

請參閱[本示例](http://ss64.org/viewtopic.php?id=2124)。 – Aacini

相關問題