2010-03-12 2 views
43

Есть ли способ скопировать действительно большой файл (с одного сервера на другой) в Powershell И отобразить его прогресс?Прогресс во время копирования большого файла (копирование и запись-прогресс?)

Есть решения для использования Write-Progress в сочетании с циклом для копирования многих файлов и отображения прогресса. Однако я не могу найти ничего, что могло бы показать прогресс одного файла.

Любые мысли?

ответ

37

Я не слышал о прогрессе с 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 
    } 
} 
+4

Интересное решение. Когда я попробовал это, я получил ошибку - Не удалось преобразовать значение «2147483648», чтобы ввести «System.Int32». Ошибка: «Значение было слишком большим или слишком маленьким для Int32». После замены [int] на [long], он отлично работал. Thanks –

+0

Это значит, что вы копируете файлы размером более 2 ГБ? Полагаю, что так. Я рад, что это работает :) – stej

+0

+1, хорошее решение – Remko

8

Не знаю, о чем я знаю. В любом случае я бы не рекомендовал использовать экземпляр для этого. Я не думаю, что он был разработан так, чтобы быть надежным, как robocopy.exe, чтобы поддерживать повторную попытку, которую вы хотели бы использовать для чрезвычайно больших копий файлов по сети.

+1

Valid точку. В этом конкретном случае я не слишком беспокоюсь о надежности. Он копирует 15-гигабайтный файл между двумя серверами на одной и той же задней плоскости. Однако в других ситуациях я бы определенно рассмотрел более надежное решение. –

17

Alternativly этот вариант использует родное окно прогресс бар ...

$FOF_CREATEPROGRESSDLG = "&H0&" 

$objShell = New-Object -ComObject "Shell.Application" 

$objFolder = $objShell.NameSpace($DestLocation) 

$objFolder.CopyHere($srcFile, $FOF_CREATEPROGRESSDLG) 
12

Я внес изменения в код из stej (который был большим, только то, что мне нужно!) Использовать больший буфер, [длинный ] для больших файлов и используется класс System.Diagnostics.Stopwatch для отслеживания прошедшего времени и оценки оставшегося времени.

Также добавлено сообщение о скорости передачи при передаче и выводе общего истекшего времени и общей скорости передачи.

Использование буфера 4 МБ (4096 * 1024 байт), чтобы стать лучше, чем собственное копирование пропускной способности Win7 с NAS на USB-накопитель на ноутбуке через Wi-Fi.

На To-Do список:

  • добавить обработку ошибок (вылов)
  • обрабатывать Get-ChildItem списка файлов в качестве входных данных
  • вложенных полос прогресса при копировании нескольких файлов (файл х у,% если итоговые данные копируются и т.д.) параметр
  • ввода для размера буфера

Вы можете использовать/улучшить :-)

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

Хороший скрипт, но он дает деление на ноль. Мне пришлось добавить: if ($ secselapsed -ne 0) { [single] $ xferrate = (($ total/$ secselapsed)/1mb); } else { [single] $ xferrate = 0.0 } – 79E09796

+0

Не что-то, что я встретил в своем ежедневном использовании этого кода, какую версию powershell вы используете? Это когда-нибудь работает для вас? Просто любопытно. Все, что делает его более надежным, отлично от меня :-) –

+0

В Powershell 2.0.1.1 он работал с перерывами, но в большинстве случаев нет. Казалось, что это может скопировать первый блок слишком быстро, а затем округлить $ secelapsed. Я добавил обновление, может кто-то сэкономит время. Еще раз спасибо, это полезный скрипт. – 79E09796

54

Похоже, что гораздо лучшее решение, чтобы просто использовать BitsTransfer, кажется, приходят OOTB на большинстве машин Windows, с помощью PowerShell 2.0 или выше.

Import-Module BitsTransfer 
Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup" 
+0

Отлично! Действительно, это также дает мне индикатор скорости (powershell). – mousio

+0

, вероятно, не будет использовать возможности BITS, если вы не вытаскиваете источник из удаленного места, но он работает плавно. – mCasamento

+0

Это потрясающе! – Tarik

6
cmd /c copy /z src dest 

не чисто PowerShell, но исполняемый в PowerShell и отображает прогресс в процентах

0

Тревор Sullivan имеет рецензию о том, как добавить команду под названием Copy-ItemWithProgress в Powershell на Robocopy.

1

Этих рекурсивные функция копирует файлы и каталоги рекурсивно из исходного пути к пути назначения

Если файл уже существует на пути назначения, он копирует их только с новыми файлами.

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

У Шона Кирни от Hey, Scripting Guy! Blog есть решение, которое я нашел, работает очень красиво.

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