大きなファイルのコピー中の進行状況 (項目のコピーと書き込みの進行状況?)
-
19-09-2019 - |
質問
PowerShellで非常に大きなファイルを(あるサーバーから別のサーバーに)コピーし、その進行状況を表示する方法はありますか?
Write-Progress をループと組み合わせて使用して、多くのファイルをコピーし、進行状況を表示するソリューションがあります。ただし、単一のファイルの進行状況を示すものが見つからないようです。
何かご意見は?
解決
私はCopy-Item
と進捗状況について聞いたことがありません。あなたが任意の外部ツールを使用したくない場合は、ストリームを試すことができます。バッファのサイズが変化し、あなたは(2キロバイトから64キロバイトまで)異なる値を試すことがあります。
function Copy-File {
param( [string]$from, [string]$to)
$ffile = [io.file]::OpenRead($from)
$tofile = [io.file]::OpenWrite($to)
Write-Progress -Activity "Copying file" -status "$from -> $to" -PercentComplete 0
try {
[byte[]]$buff = new-object byte[] 4096
[int]$total = [int]$count = 0
do {
$count = $ffile.Read($buff, 0, $buff.Length)
$tofile.Write($buff, 0, $count)
$total += $count
if ($total % 1mb -eq 0) {
Write-Progress -Activity "Copying file" -status "$from -> $to" `
-PercentComplete ([int]($total/$ffile.Length* 100))
}
} while ($count -gt 0)
}
finally {
$ffile.Dispose()
$tofile.Dispose()
Write-Progress -Activity "Copying file" -Status "Ready" -Completed
}
}
他のヒント
それはちょうどBitsTransferを使用するためのより良い解決策のように思える、それはPowerShellの2.0以上で、ほとんどのWindowsマシン上でOOTBを来ているようだ。
Import-Module BitsTransfer
Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup"
Alternativlyこのオプションは、ネイティブウィンドウのプログレスバーを使用しています...
$FOF_CREATEPROGRESSDLG = "&H0&"
$objShell = New-Object -ComObject "Shell.Application"
$objFolder = $objShell.NameSpace($DestLocation)
$objFolder.CopyHere($srcFile, $FOF_CREATEPROGRESSDLG)
cmd /c copy /z src dest
はない純粋なPowerShellのが、実行PowerShellであり、それはパーセントで進行状況を表示する
stej のコードを修正して (これは素晴らしく、まさに必要なものでした!)、より大きなバッファー (より大きなファイルには [長い]) を使用し、System.Diagnostics.Stopwatch クラスを使用して経過時間を追跡し、残り時間を推定しました。
また、転送中の転送速度のレポートと、全体の経過時間と全体の転送速度の出力も追加されました。
4MB (4096*1024 バイト) バッファを使用すると、Wi-Fi 経由で NAS からラップトップの USB スティックにコピーする Win7 ネイティブ スループットよりも向上します。
ToDo リストについて:
- エラー処理(キャッチ)を追加
- get-childitem ファイルリストを入力として処理します
- 複数のファイルをコピーするときにネストされた進行状況バー(yのファイルx、総データがコピーされた場合など)
- バッファサイズの入力パラメータ
自由に使用/改善してください:-)
function Copy-File {
param( [string]$from, [string]$to)
$ffile = [io.file]::OpenRead($from)
$tofile = [io.file]::OpenWrite($to)
Write-Progress `
-Activity "Copying file" `
-status ($from.Split("\")|select -last 1) `
-PercentComplete 0
try {
$sw = [System.Diagnostics.Stopwatch]::StartNew();
[byte[]]$buff = new-object byte[] (4096*1024)
[long]$total = [long]$count = 0
do {
$count = $ffile.Read($buff, 0, $buff.Length)
$tofile.Write($buff, 0, $count)
$total += $count
[int]$pctcomp = ([int]($total/$ffile.Length* 100));
[int]$secselapsed = [int]($sw.elapsedmilliseconds.ToString())/1000;
if ( $secselapsed -ne 0 ) {
[single]$xferrate = (($total/$secselapsed)/1mb);
} else {
[single]$xferrate = 0.0
}
if ($total % 1mb -eq 0) {
if($pctcomp -gt 0)`
{[int]$secsleft = ((($secselapsed/$pctcomp)* 100)-$secselapsed);
} else {
[int]$secsleft = 0};
Write-Progress `
-Activity ($pctcomp.ToString() + "% Copying file @ " + "{0:n2}" -f $xferrate + " MB/s")`
-status ($from.Split("\")|select -last 1) `
-PercentComplete $pctcomp `
-SecondsRemaining $secsleft;
}
} while ($count -gt 0)
$sw.Stop();
$sw.Reset();
}
finally {
write-host (($from.Split("\")|select -last 1) + `
" copied in " + $secselapsed + " seconds at " + `
"{0:n2}" -f [int](($ffile.length/$secselapsed)/1mb) + " MB/s.");
$ffile.Close();
$tofile.Close();
}
}
私の知るないこと。私はこのとにかくためにコピーアイテムを使用することはお勧めしません。私はあなたがネットワーク上で非常に大きなファイルのコピーをしたいと思うのリトライをサポートするためのRobocopy.exeのように堅牢に設計されていないと思います。
この再帰関数をコピーし、ファイルとディレクトリ
新しいファイルのみとファイルが既に先のパス上に存在する場合、それをコピーして。
Function Copy-FilesBitsTransfer(
[Parameter(Mandatory=$true)][String]$sourcePath,
[Parameter(Mandatory=$true)][String]$destinationPath,
[Parameter(Mandatory=$false)][bool]$createRootDirectory = $true)
{
$item = Get-Item $sourcePath
$itemName = Split-Path $sourcePath -leaf
if (!$item.PSIsContainer){ #Item Is a file
$clientFileTime = Get-Item $sourcePath | select LastWriteTime -ExpandProperty LastWriteTime
if (!(Test-Path -Path $destinationPath\$itemName)){
Start-BitsTransfer -Source $sourcePath -Destination $destinationPath -Description "$sourcePath >> $destinationPath" -DisplayName "Copy Template file" -Confirm:$false
if (!$?){
return $false
}
}
else{
$serverFileTime = Get-Item $destinationPath\$itemName | select LastWriteTime -ExpandProperty LastWriteTime
if ($serverFileTime -lt $clientFileTime)
{
Start-BitsTransfer -Source $sourcePath -Destination $destinationPath -Description "$sourcePath >> $destinationPath" -DisplayName "Copy Template file" -Confirm:$false
if (!$?){
return $false
}
}
}
}
else{ #Item Is a directory
if ($createRootDirectory){
$destinationPath = "$destinationPath\$itemName"
if (!(Test-Path -Path $destinationPath -PathType Container)){
if (Test-Path -Path $destinationPath -PathType Leaf){ #In case item is a file, delete it.
Remove-Item -Path $destinationPath
}
New-Item -ItemType Directory $destinationPath | Out-Null
if (!$?){
return $false
}
}
}
Foreach ($fileOrDirectory in (Get-Item -Path "$sourcePath\*"))
{
$status = Copy-FilesBitsTransfer $fileOrDirectory $destinationPath $true
if (!$status){
return $false
}
}
}
return $true
}
ショーン・カーニー<のhref = "https://blogs.technet.microsoft.com/heyscriptingguy/2015/12/20/build-a-better-copy-item-cmdlet-2/" のrelから=」 nofollowをnoreferrer ">さん、こんにちは!ブログには、私はかなりうまく動作したソリューションを持っています。
Function Copy-WithProgress
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$Source,
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$Destination
)
$Source=$Source.tolower()
$Filelist=Get-Childitem "$Source" –Recurse
$Total=$Filelist.count
$Position=0
foreach ($File in $Filelist)
{
$Filename=$File.Fullname.tolower().replace($Source,'')
$DestinationFile=($Destination+$Filename)
Write-Progress -Activity "Copying data from '$source' to '$Destination'" -Status "Copying File $Filename" -PercentComplete (($Position/$total)*100)
Copy-Item $File.FullName -Destination $DestinationFile
$Position++
}
}
そして、それを使用します:
Copy-WithProgress -Source $src -Destination $dest
トレバー・サリバンは、Robocopyの上でPowerShellのにをコピー-ItemWithProgressと呼ばれるコマンドを追加する方法についての書き込みアップを持っています。
古い話題を取り上げるのは嫌いですが、この投稿は非常に役立つと思いました。stej によるスニペットと Graham Gold による改良、さらに Nacht による BITS の提案に対してパフォーマンス テストを実行した後、私は次のことを結論付けました。
- 私 本当に 時間の見積もりと速度の読み取りに関するグラハムのコマンドが好きでした。
- 私も 本当に 転送方法として BITS を使用すると速度が大幅に向上することが気に入りました。
二人の間で決断を迫られる…。Start-BitsTransfer が非同期モードをサポートしていることがわかりました。したがって、これが 2 つをマージした結果です。
function Copy-File {
param([string]$from, [string]$to)
try {
$job = Start-BitsTransfer -Source $from -Destination $to `
-Description "Moving: $from => $to" `
-DisplayName "Backup" -Asynchronous
# Start stopwatch
$sw = [System.Diagnostics.Stopwatch]::StartNew()
Write-Progress -Activity "Connecting..."
while ($job.JobState.ToString() -ne "Transferred") {
switch ($job.JobState.ToString()) {
"Connecting" {
break
}
"Transferring" {
$pctcomp = ($job.BytesTransferred / $job.BytesTotal) * 100
$elapsed = ($sw.elapsedmilliseconds.ToString()) / 1000
if ($elapsed -eq 0) {
$xferrate = 0.0
} else {
$xferrate = (($job.BytesTransferred / $elapsed) / 1mb);
}
if ($job.BytesTransferred % 1mb -eq 0) {
if ($pctcomp -gt 0) {
$secsleft = ((($elapsed / $pctcomp) * 100) - $elapsed)
} else {
$secsleft = 0
}
Write-Progress -Activity ("Copying file '" + ($PathName.Split("\") | Select -last 1) + "' @ " + "{0:n2}" -f $xferrate + "MB/s") `
-PercentComplete $pctcomp `
-SecondsRemaining $secsleft
}
break
}
"Transferred" {
break
}
Default {
throw $job.JobState.ToString() + " unexpected BITS state."
}
}
}
$sw.Stop()
$sw.Reset()
} finally {
Complete-BitsTransfer -BitsJob $job
Write-Progress -Activity "Completed" -Completed
}
}