web-dev-qa-db-ja.com

Windowsの巨大なファイルの最後のn行またはバイトを取得します(Unixのテールなど)。時間のかかるオプションを避ける

Windows 7では、巨大なファイルの最後のn行(1〜4 Gb)を取得する必要があります。企業の制限により、組み込みではないコマンドは実行できません。問題は、見つけたすべてのソリューションがファイル全体を読み取るように見えるため、非常に遅いことです。

これを迅速に達成できますか?

ノート:

  1. 最初のn行をすばやく取得できました。
  2. 最後のnバイトを取得できれば問題ありません。 (私はこれを使用しました https://stackoverflow.com/a/18936628/2707864 最初のnバイト)。

ここでの解決策 Windows PowershellのUnix tail同等のコマンド は機能しませんでした。 -waitを使用しても高速にはなりません。 -tailがありません(それが高速に動作するかどうかわかりません)。

PS:headtailには関連する質問がかなりありますが、速度の問題に焦点を合わせていません。したがって、役に立つ回答または受け入れられている回答は、ここでは役に立ちません。例えば。、

Windowsの 'tail'コマンドに相当

txtファイルの最後の10行を表示するCMD.EXEバッチスクリプト

単一のWindowsコマンドを使用してファイルからN行を抽出

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

ファイルの最初のx MBを取得するPowerShell

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

17
sancho.s

これはどうですか(デモ用に最後の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改行文字シーケンスよりも多くなるまで、部分ごとにファイルを読み込みます。

$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。これは、エラー処理や最適化を行わない非常に汚いコードです。

14
Aziz Kabyshev

PowerShell 3以降を使用している場合は、-TailGet-Contentパラメーターを使用して、最後のn行を取得できます。

Get-content -tail 5 PATH_TO_FILE;

ローカルSSDの34MBテキストファイルでは、get-content |select -last 5の8.5秒に対して1ミリ秒で返されました

47
alroc

Aziz Kabyshevによる素晴らしい答え で、速度の問題を解決し、いくつかのグーグルで、私はこのスクリプトを使用することになりました

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

(おかげで バッチファイルからPowerShellスクリプトを実行する方法 )。

3
sancho.s

これは答えではなく、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%
1
Aacini