【发布时间】:2014-04-20 01:47:27
【问题描述】:
使用 Powershell 连接二进制文件的最佳方法是什么? 我更喜欢一种简单易记且执行速度快的单行代码。
我想到的最好的是:
gc -Encoding Byte -Path ".\File1.bin",".\File2.bin" | sc -Encoding Byte new.bin
这似乎工作正常,但对于大文件来说非常慢。
【问题讨论】:
标签: powershell
使用 Powershell 连接二进制文件的最佳方法是什么? 我更喜欢一种简单易记且执行速度快的单行代码。
我想到的最好的是:
gc -Encoding Byte -Path ".\File1.bin",".\File2.bin" | sc -Encoding Byte new.bin
这似乎工作正常,但对于大文件来说非常慢。
【问题讨论】:
标签: powershell
您采用的方法是我在 PowerShell 中采用的方法。但是,您应该使用 -ReadCount 参数来提高性能。您还可以利用位置参数来进一步缩短它:
gc File1.bin,File2.bin -Encoding Byte -Read 512 | sc new.bin -Encoding Byte
编者注:在跨平台的PowerShell (Core) 版本(版本6 及以上)中,现在必须使用-AsByteStream 而不是-Encoding Byte;此外,Set-Content cmdlet 的 sc 别名已被删除。
关于 -ReadCount 参数的使用,我不久前写了一篇博文,大家可能会觉得有用 - Optimizing Performance of Get Content for Large Files。
【讨论】:
Get-Content: Cannot process argument transformation on parameter 'Encoding'. 'Byte' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter 'name')
它不是 Powershell,但如果你有 Powershell,你也有命令提示符:
copy /b 1.bin+2.bin 3.bin
正如 Keith Hill 指出的,如果你真的需要从 Powershell 内部运行它,你可以使用:
cmd /c copy /b 1.bin+2.bin 3.bin
【讨论】:
copy 支持通配符。所以copy /b *.bin out.bin 将连接你所有的 bin 文件,输出会非常快(即比使用 PowerShell 快得多)。
我最近遇到了类似的问题,我想将两个大 (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 秒(OK)【讨论】:
性能很大程度上取决于所使用的缓冲区大小。默认情况下,它们相当小。连接 2x2GB 文件我会占用大约 256kb 的缓冲区。变大有时可能会失败,变小可能会导致吞吐量低于驱动器的能力。
gc 将与 -ReadCount 一起使用,而不仅仅是 -Read (PowerShell 5.0):
gc -ReadCount 256KB -Path $infile -Encoding Byte | ...
另外,我发现 Add-Content 更好,并且可以逐个文件处理大量小文件,因为只传输适量的数据 (200MB) .
虽然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()
}
【讨论】: