Подтвердить что ты не робот

Быстрые и простые двоичные файлы конкатенации в Powershell

Какой лучший способ объединить двоичные файлы с помощью Powershell? Я бы предпочел, чтобы один-лайнер был прост для запоминания и быстрого выполнения.

Лучшее, что я придумал, это:

gc -Encoding Byte -Path ".\File1.bin",".\File2.bin" | sc -Encoding Byte new.bin

Кажется, что он работает нормально, но с большими файлами он очень медленный.

4b9b3361

Ответ 1

Подход, который вы принимаете, - это то, как я буду делать это в PowerShell. Однако вы должны использовать параметр -ReadCount для улучшения perf. Вы также можете воспользоваться позиционными параметрами, чтобы сократить это еще больше:

gc File1.bin,File2.bin -Enc Byte -Read 512 | sc new.bin -Enc Byte

Что касается использования параметра -ReadCount, я сделал сообщение в блоге об этом некоторое время назад, что люди могут найти полезные - Оптимизация производительности Get Content for Large файлы.

Ответ 2

Это не Powershell, но если у вас есть Powershell, у вас также есть командная строка:

copy /b 1.bin+2.bin 3.bin

Как заметил Кейт Хилл, если вам действительно нужно запустить его изнутри Powershell, вы можете использовать:

cmd /c copy /b 1.bin+2.bin 3.bin 

Ответ 3

У меня была аналогичная проблема в последнее время, когда я хотел добавить два больших (2 ГБ) файла в один файл (4 ГБ).

Я попытался настроить параметр -ReadCount для Get-Content, однако я не смог его улучшить для больших файлов.

Я пошел со следующим решением:

function Join-File (
    [parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
    [string[]] $Path,
    [parameter(Position=1,Mandatory=$true)]
    [string] $Destination
)
{
    write-verbose "Join-File: Open Destination1 $Destination"
    $OutFile = [System.IO.File]::Create($Destination)
    foreach ( $File in $Path ) {
        write-verbose "   Join-File: Open Source $File"
        $InFile = [System.IO.File]::OpenRead($File)
        $InFile.CopyTo($OutFile)
        $InFile.Dispose()
    }
    $OutFile.Dispose()
    write-verbose "Join-File: finished"
} 

Производительность:

  • cmd.exe /c copy file1+file2 File3 около 5 секунд (лучше всего)
  • gc file1,file2 |sc file3 около 1100 секунд (yuck)
  • join-file File1,File2 File3 около 16 секунд (OK)

Ответ 4

Производительность очень сильно зависит от используемого размера буфера. По умолчанию они довольно малы. Объединяя файлы 2x2GB, я бы взял буферизацию размером около 256 КБ. Увеличение размера может иногда заканчиваться неудачно, меньше, и вы получите меньшую пропускную способность, чем ваш диск способен.

С gc, который был бы с -ReadCount не просто -Read (PowerShell 5.0):

gc -ReadCount 256KB -Path $infile -Encoding Byte | ...

Плюс я нашел, что Add-Content лучше и идет по файловому файлу для большого количества небольших файлов, потому что для передачи только небольшого количества данных (200 МБ) я обнаружил, что мой компьютер работает, PowerShell замерзает и процессор полностью.

Хотя Add-Content случайным образом терпит неудачу несколько раз для нескольких сотен файлов с ошибкой в ​​файле назначения, который используется, поэтому я добавил цикл while и try catch:

# Empty the file first
sc -Path "$path\video.ts" -Value @() -Encoding Byte 
$tsfiles | foreach {    
    while ($true) {
        try { # I had -ReadCount 0 because the files are smaller than 256KB
            gc -ReadCount 0 -Path "$path\$_" -Encoding Byte | `
                Add-Content -Path "$path\video.ts" -Encoding Byte -ErrorAction Stop
            break;
        } catch {
        }
    }
}

Использование потока файлов намного быстрее. Вы не можете указать размер буфера с помощью [System.IO.File]::Open, но вы можете с new [System.IO.FileStream] следующим образом:

# $path = "C:\"
$ins = @("a.ts", "b.ts")
$outfile = "$path\out.mp4"
$out = New-Object -TypeName "System.IO.FileStream" -ArgumentList @(
    $outfile, 
    [System.IO.FileMode]::Create,
    [System.IO.FileAccess]::Write,
    [System.IO.FileShare]::None,
    256KB,
    [System.IO.FileOptions]::None)
try {
    foreach ($in in $ins) {
        $fs = New-Object -TypeName "System.IO.FileStream" -ArgumentList @(
            "$path\$in", 
            [System.IO.FileMode]::Open,
            [System.IO.FileAccess]::Read,
            [System.IO.FileShare]::Read,
            256KB,
            [System.IO.FileOptions]::SequentialScan)
        try {
            $fs.CopyTo($out)
        } finally {
            $fs.Dispose()
        }
    }
} finally {
    $out.Dispose()
}