2009-11-23 21 views
21

¿Cuál es la mejor forma de concatenar archivos binarios usando Powershell? Prefiero un diseño único que sea fácil de recordar y rápido de ejecutar.Archivos de concatenación binarios rápidos y simples en Powershell

El mejor que he llegado con es:

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

Esto parece funcionar bien, pero es terriblemente lento con archivos de gran tamaño.

Respuesta

25

El enfoque que está tomando es la manera en que lo haría en PowerShell. Sin embargo, debe usar el parámetro -ReadCount para mejorar el rendimiento. También puede tomar ventaja de los parámetros de posición para acortar aún más este:

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

En cuanto al uso del parámetro -ReadCount, hice un post sobre este hace un tiempo que la gente pueda resultar de utilidad - Optimizing Performance of Get Content for Large Files.

+2

Acabo de ejecutar esto en mis archivos de ejemplo y el comando pasó de tomar 9 minutos a 3 segundos con la inclusión del parámetro -read. Esto está en una unidad x25m. Bonito. Consigues mi aceptar. – FkYkko

+0

Acabo de utilizar su one-liner para unir una iso de 4.4 gb distribuida en más de 23 archivos. Reensamblé bien el archivo y tardé 35 minutos en mi computadora portátil usando bloques de 1024 bytes. –

+0

Supongo que esto funciona porque la pipa está enviando objetos .net a sc? Cuando traté de canalizar datos binarios a un programa c, noté que solo obtuve los primeros 7 bits de cada byte, ya que "|" invocó la codificación. – johnnycrash

20

No es Powershell, pero si usted tiene Powershell también tienen el símbolo del sistema:

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

Como Keith Hill señaló, si realmente se necesita para ejecutarlo desde el interior de PowerShell, puede usar:

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

copia es una orden intrínseco en cmd.exe. Tendría que ejecutar cmd/c copy/b 1.bin + 2.bin 3.bin –

+0

Una solución simple y agradable, funciona en cualquier computadora con Windows. Upvoted pero acepto a Keith ya que solicité la versión PS. Thx – FkYkko

+3

Tenga en cuenta también que 'copy' admite comodines. Entonces 'copy/b * .bin out.bin' concatenará todos sus archivos bin y la salida será muy rápida (es decir, mucho más rápido que con PowerShell). –

3

Tuve un problema similar recientemente, donde quería agregar dos archivos grandes (2GB) en un solo archivo (4GB).

Intenté ajustar el parámetro -ReadCount para Get-Content; sin embargo, no pude conseguir que mejorara mi rendimiento para los archivos de gran tamaño.

Fui con la siguiente solución:

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

Rendimiento:

  • cmd.exe /c copy file1+file2 File3 unos 5 segundos (mejor)
  • gc file1,file2 |sc file3 alrededor de 1100 segundos (puaj)
  • join-file File1,File2 File3 alrededor 16 segundos (OK)
+0

La copia de cmd.exe es muchas veces más rápida que los cmdlets PS nativos: 1.2MB/s versus> 120Mb/s. No es sorprendente teniendo en cuenta cómo Get-Content funciona incluso con el parámetro -ReadCound –

1

El rendimiento depende en gran medida del tamaño del búfer utilizado. Esas son bastante pequeñas por defecto. Concatenar archivos de 2x2GB tomaría un buffer de aproximadamente 256kb. En ocasiones, una mayor potencia puede fallar, ser más pequeña y obtener un rendimiento menor de lo que su unidad es capaz de hacer.

Con gc que estaría con -ReadCount no simplemente -Read (PowerShell 5.0):

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

Además me encontré Add-Content a ser mejor y archivo por archivo en busca de un montón de archivos pequeños, porque las tuberías solo una cantidad moderada de datos (200 MB) encontré que mi computadora estaba en funcionamiento, la congelación de PowerShell y la CPU estaban llenos.

Aunque Add-Content falla al azar un par de veces para unos pocos cientos de archivos con un error sobre el archivo de destino de estar en uso, por lo que añade un bucle while y un intento de captura:

# 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 { 
     } 
    } 
} 

usando una secuencia de archivo es mucho más rápido aún No se puede especificar un tamaño de búfer con [System.IO.File]::Open pero puede hacerlo con new [System.IO.FileStream] así:

# $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() 
} 
+0

. Uno está adivinando que este es un método muy similar utilizado por el comando de copiado cmd.exe. –