Powershell中快速简单的二进制连接文件

mccptt67  于 2023-10-18  发布在  Shell
关注(0)|答案(5)|浏览(131)

使用PowerShell连接二进制文件的最佳方法是什么?我更喜欢一个简单的记忆和快速执行的一行程序。
我想到的最好的办法是:

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

这似乎工作正常,但对于大文件非常慢。

omvjsjqw

omvjsjqw1#

你采取的方法是我在PowerShell中使用的方法。但是,您应该使用-ReadCount参数来提高性能。您还可以利用位置参数来进一步缩短此时间:

gc File1.bin,File2.bin -Encoding Byte -Read 512 | sc new.bin -Encoding Byte
  • 编者按 *:在跨平台PowerShell (Core)版本(版本6及以上)中,现在必须使用-AsByteStream而不是-Encoding Byte;此外,Set-Content小程序的sc别名已被删除。

关于-ReadCount参数的使用,我在不久前写了一篇博客文章,人们可能会发现-Optimizing Performance of Get Content for Large Files很有用。

soat7uwm

soat7uwm2#

它不是PowerShell,但如果你有PowerShell,你也有命令提示符:

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

正如基思希尔指出的那样,如果你真的需要从PowerShell内部运行它,你可以用途:

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

mrphzbgm3#

我最近遇到了类似的问题,我想将两个大文件(2GB)附加到一个文件(4GB)中。
我尝试调整Get-Content的-ReadCount参数,但无法提高处理大文件的性能。
我采用了以下解决方案:

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秒(讨厌)
  • join-file File1,File2 File3约16秒(正常)
50few1ms

50few1ms4#

性能在很大程度上取决于所使用的缓冲区大小。默认情况下,这些都相当小。连接2x2GB的文件,我需要一个大约256kb的缓冲区大小。变大有时可能会失败,变小,你会得到更少的吞吐量比你的驱动器的能力。
使用gc,将与-ReadCount一起使用,而不仅仅是-Read(PowerShell 5.0):

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

另外,我发现Add-Content更好,对于很多小文件来说,一个文件接一个文件,因为管道传输的数据量很小(200MB),我发现我的电脑正在运行,PowerShell冻结,CPU满负荷运行。
尽管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()
}
w46czmvw

w46czmvw5#

你也可以用这个方法连接任何类型的文件。
不是那么快,只是另一种方式来做它

cmd /c type file1 file2 file3 file4 > outfile

或者,如果存在多个复制任务,则可以使用作业来加快速度。

$jobs=@()
$Files=@( @("d:\path\app1.rar","d:\path\app2.rar","d:\path\app3.rar","d:\path\appOutFile.rar"),@("c:\users\user\desktop\file1.iso","c:\users\user\desktop\file2.iso","c:\users\user\desktop\file3.iso","c:\users\user\desktop\isoOutFile.iso"),@("c:\users\user\video\video1.mp4","c:\users\user\video\video2.mp4","c:\users\user\video\mp4OutFile.mp4"))
$totalMb=0
foreach($Group in $Files){
    $sources=$Group|select -first ($Group.Length-1)
    $sources|%{ $totalMb+=(get-Item $_).Length/1mb }
    $sources=$(($sources -replace "^|$",[char]34)-join"+")
    $destination=[char]34+($Group|select -last 1)+[char]34
    cmd /c type nul> $destination
    $jobs+=start-job -scriptBlock { param($src,$dst) &cmd.exe "/c copy /b $src $dst" } -arg $sources,$destination 
    # $jobs+=start-job -scriptBlock { param($src,$dst) &cmd.exe "/c type $($src -replace '`"\+`"','`" `"') > $dst" } -arg $sources,$destination
}

"Total MegaBytes To Concatenate is: $( [math]::Truncate($totalMb) )MB"
while ( $($jobs|% {$_.state -eq "running"}) -contains $true ) {"still concatenating files...";start-sleep -s 2}

"jobs finished !!!";$jobs|receive-job;$jobs|remove-job -force

相关问题