Прогресс во время копирования большого файла (Copy-Item & Write-Progress?)

Есть ли способ скопировать действительно большой файл (с одного сервера на другой) в PowerShell и отобразить его ход?

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

Есть предположения?


person Jason Jarrett    schedule 12.03.2010    source источник


Ответы (11)


Я не слышал о прогрессе с 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
        [long]$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 ([long]($total * 100 / $ffile.Length))
            }
        } while ($count -gt 0)
    }
    finally {
        $ffile.Dispose()
        $tofile.Dispose()
        Write-Progress -Activity "Copying file" -Status "Ready" -Completed
    }
}
person stej    schedule 12.03.2010
comment
Интересное решение. Когда я попробовал это, я получил сообщение об ошибке - Невозможно преобразовать значение 2147483648 в тип System.Int32. Ошибка: значение слишком велико или слишком мало для Int32. После замены [int] на [long] все заработало отлично. Спасибо - person Jason Jarrett; 23.03.2010
comment
Это означает, что вы копируете файлы размером более 2 ГБ? Полагаю, что так. Я рад, что это работает :) - person stej; 23.03.2010
comment
+1 простые решения лучше! Я копирую большие (8 ГБ+) файлы из одного сетевого расположения в другое... гигабитная сеть... (только указание)... использование блоков по 1 МБ означает, что сетевой адаптер работает примерно на 50% (я подозреваю, что на нашем коммутаторе есть дросселирование) ... хотя блоки меньшего размера были не очень хороши. - person Aidanapword; 11.03.2013
comment
Небольшое замечание .NETy: функция finally должна вызывать Dispose(), а не Close(). Однако хорошее решение. Мне грустно, что нет встроенного прогресса. - person TheXenocide; 07.05.2013
comment
Верно, отсутствует оператор using в PowerShell :) - person stej; 09.05.2013
comment
@TheXenocide Почему? Stream.Close() просто вызывает Stream.Dispose()? referencesource.microsoft.com/#mscorlib/system/io/stream.cs< /а> - person Keith Hill; 25.09.2014
comment
Это верно, но обычно всякий раз, когда объект IDisposable, я вызываю Dispose(). Имо это безопаснее, и вам не нужно знать внутренности. - person stej; 25.09.2014
comment
Ну, это скорее шутка программирования, чем сценарий (если вы решите различать), но с точки зрения информатики: вы полагаетесь на детали внутренней реализации объекта, которые не гарантированы и могут измениться в любое время. , а также не в соответствии с установленной схемой публичного контракта. Это нарушает основной принцип объектно-ориентированного проектирования, а также игнорирует общедоступный контракт IDisposable (о существовании которого вы должны знать), который имеет хорошо зарекомендовавшие себя передовые методы, согласно которым его всегда следует удалять. - person TheXenocide; 25.09.2014
comment
Вы должны заменить [io.file]::OpenWrite($to) на [io.file]::Create($to), иначе он не будет правильно перезаписывать файлы. - person Scott Chamberlain; 16.10.2015
comment
Что значит правильно? - person stej; 22.10.2015
comment
Если кто-то пытается заставить это работать с PSDrive, вам нужно изменить инициализацию $ffile на (Get-ChildItem $from).OpenRead() и инициализацию $tofile на (new-item $to -Type File -Force ).ОткрытьЗапись() - person Nick; 18.05.2017
comment
Я изменил [int]$total на [long]$total, чтобы учитывать большие файлы, как предложил Джейсон. - person VeeTheSecond; 31.07.2020

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

Import-Module BitsTransfer
Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup"
person Nacht    schedule 09.09.2014
comment
Здорово! Действительно, это также дает мне индикатор прогресса (powershell). - person mousio; 24.11.2014
comment
он, вероятно, не будет использовать возможности BITS, если вы не извлекаете исходный код из удаленного места, но он работает гладко. - person mCasamento; 07.03.2015
comment
Именно то, что у меня было после - отлично работало и дает индикатор выполнения! - person Shawson; 04.01.2016
comment
Я использовал это для копирования виртуальных машин в 2012 R2 Hyper-V бесплатно. - person johnny; 27.08.2016
comment
Я использую это на сервере Hyper-V с минимальным графическим интерфейсом, и он отлично работает! - person MemphiZ; 05.12.2016
comment
Это должен быть главный ответ. - person tyteen4a03; 06.09.2017
comment
По крайней мере, не работает с Powershell Core 6.1.0. Я получаю сообщение об ошибке импорта, в котором говорится, что BitsTransfer.psd1 не поддерживает текущую версию PowerShell «Core». - person Manuzor; 06.12.2018
comment
Странно, когда я запускаю команду, вообще ничего не происходит без вывода? Я использую, например, -Source \\comp1\c$\folder -Destionation \\comp2\c$\folder , есть идеи, что может быть не так? У меня есть доступ к обеим папкам, там нет проблем. Если я использую элемент копирования, он работает, но, очевидно, без прогресса. - person Rakha; 29.03.2019
comment
В Ps 5.1 я получаю: user is not registered in Network. Я копирую из сетевого ресурса. Я использую 2 параметра source и Destination. - person Timo; 07.08.2019

В качестве альтернативы эта опция использует собственный индикатор выполнения Windows...

$FOF_CREATEPROGRESSDLG = "&H0&"

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

$objFolder = $objShell.NameSpace($DestLocation) 

$objFolder.CopyHere($srcFile, $FOF_CREATEPROGRESSDLG)
person Chris M    schedule 13.07.2012
comment
Это блестяще, как указать флаг ALWAYS OVERWRITE для этого метода, возможно ли это? Поэтому он не запрашивает, когда файлы существуют. - person Rakha; 06.06.2019
comment
@Rakha, вам просто нужно передать 16 в качестве параметра sec для функции CopyHere следующим образом: $objFolder.CopyHere($srcFile, 16) - person Ram; 17.07.2021

cmd /c copy /z src dest

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

person Jirka Jr.    schedule 05.01.2015
comment
Отличный ответ. Я также использовал этот ответ для вывода прогресса. - person ZX9; 07.01.2020

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

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

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

В списке дел:

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

Не стесняйтесь использовать/улучшать :-)

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();
    }
}
person Graham Gold    schedule 01.12.2012
comment
Хороший скрипт, но он дает деление на ноль. Мне пришлось добавить: if ($secselapsed -ne 0) { [single]$xferrate = (($total/$secselapsed)/1mb); } иначе {[одиночный]$xferrate = 0.0} - person 79E09796; 20.06.2013
comment
Не то, с чем я сталкивался в своем повседневном использовании этого кода, какую версию powershell вы используете? Это когда-нибудь работает для вас? Просто любопытно. Все, что делает его более надежным, меня устраивает :-) - person Graham Gold; 22.06.2013
comment
В Powershell 2.0.1.1 он работал с перерывами, но чаще всего нет. Казалось, что он может слишком быстро копировать первый блок, а затем округлять значение $secelapsed в меньшую сторону. Я поставил обновление, может сэкономит кому-то время. Еще раз спасибо, это полезный скрипт. - person 79E09796; 25.06.2013
comment
Я должен @stej за оригинальный код, который я адаптировал, но спасибо :-) - person Graham Gold; 25.06.2013
comment
Хороший скрипт, но ошибка деления на ноль есть в строке: {0:n2} -f [int](($ffile.length/$secselapsed)/1mb) + MB/s.); Вы проверяете $secselapsed -eq 0 выше в сценарии, но не в этот момент. - person Sako73; 03.03.2015
comment
Вы должны заменить [io.file]::OpenWrite($to) на [io.file]::Create($to), иначе он не будет правильно перезаписывать файлы. - person Scott Chamberlain; 16.10.2015
comment
У @ScottChamberlain никогда не было ни одной ошибки перезаписи с помощью метода ::OpenWrite, но я буду иметь это в виду, если я это сделаю. - person Graham Gold; 18.10.2015
comment
@GrahamGold Скопируйте файл, затем скопируйте второй файл с тем же именем, но с меньшим размером файла. В результирующем файле конец первого файла будет присоединен после конца второго файла. - person Scott Chamberlain; 18.10.2015
comment
@ScottChamberlain объясняет, почему я не стал бы использовать его при использовании приведенного выше кода - этот сценарий невозможен в сценарии, в котором я его использую, если во время выполнения выбрана опция перезаписи, то каждый существующий файл переименовывается как временный файл имя перед копированием нового файла, затем временный файл удаляется по завершении копирования. - person Graham Gold; 18.10.2015
comment
Как заставить Start-BitsTransfer (на самом деле, Write-Progress) регистрировать обновления в виде текста в стандартном выводе, а не только в индикаторе выполнения PowerShell? - person John Zabroski; 12.02.2018

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

person Keith Hill    schedule 12.03.2010
comment
Действительная точка. В данном конкретном случае я не слишком беспокоюсь о надежности. Он копирует 15-гигабайтный файл между двумя серверами на одной задней панели. Однако в других ситуациях я бы определенно рассмотрел более надежное решение. - person Jason Jarrett; 23.03.2010

я обнаружил, что ни один из приведенных выше примеров не соответствует моим потребностям, я хотел скопировать каталог с подкаталогами, проблема в том, что в моем исходном каталоге было слишком много файлов, поэтому я быстро достиг предела файла BITS (у меня было> 1500 файлов), а также общий каталог размер был довольно большим.

я нашел функцию, использующую robocopy, которая была хорошей отправной точкой на https://keithga.wordpress.com/2014/06/23/copy-itemwithprogress/, однако я обнаружил, что он недостаточно надежен, он не обрабатывал конечные косые черты, пробелы изящно и не останавливал копирование, когда сценарий был остановлен.

Вот моя доработанная версия:

function Copy-ItemWithProgress
{
    <#
    .SYNOPSIS
    RoboCopy with PowerShell progress.

    .DESCRIPTION
    Performs file copy with RoboCopy. Output from RoboCopy is captured,
    parsed, and returned as Powershell native status and progress.

    .PARAMETER Source
    Directory to copy files from, this should not contain trailing slashes

    .PARAMETER Destination
    DIrectory to copy files to, this should not contain trailing slahes

    .PARAMETER FilesToCopy
    A wildcard expresion of which files to copy, defaults to *.*

    .PARAMETER RobocopyArgs
    List of arguments passed directly to Robocopy.
    Must not conflict with defaults: /ndl /TEE /Bytes /NC /nfl /Log

    .PARAMETER ProgressID
    When specified (>=0) will use this identifier for the progress bar

    .PARAMETER ParentProgressID
    When specified (>= 0) will use this identifier as the parent ID for progress bars
    so that they appear nested which allows for usage in more complex scripts.

    .OUTPUTS
    Returns an object with the status of final copy.
    REMINDER: Any error level below 8 can be considered a success by RoboCopy.

    .EXAMPLE
    C:\PS> .\Copy-ItemWithProgress c:\Src d:\Dest

    Copy the contents of the c:\Src directory to a directory d:\Dest
    Without the /e or /mir switch, only files from the root of c:\src are copied.

    .EXAMPLE
    C:\PS> .\Copy-ItemWithProgress '"c:\Src Files"' d:\Dest /mir /xf *.log -Verbose

    Copy the contents of the 'c:\Name with Space' directory to a directory d:\Dest
    /mir and /XF parameters are passed to robocopy, and script is run verbose

    .LINK
    https://keithga.wordpress.com/2014/06/23/copy-itemwithprogress

    .NOTES
    By Keith S. Garner ([email protected]) - 6/23/2014
    With inspiration by Trevor Sullivan @pcgeek86
    Tweaked by Justin Marshall - 02/20/2020

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$Source,
        [Parameter(Mandatory=$true)]
        [string]$Destination,
        [Parameter(Mandatory=$false)]
        [string]$FilesToCopy="*.*",
        [Parameter(Mandatory = $true,ValueFromRemainingArguments=$true)] 
        [string[]] $RobocopyArgs,
        [int]$ParentProgressID=-1,
        [int]$ProgressID=-1
    )

    #handle spaces and trailing slashes
    $SourceDir = '"{0}"' -f ($Source -replace "\\+$","")
    $TargetDir = '"{0}"' -f ($Destination -replace "\\+$","")


    $ScanLog  = [IO.Path]::GetTempFileName()
    $RoboLog  = [IO.Path]::GetTempFileName()
    $ScanArgs = @($SourceDir,$TargetDir,$FilesToCopy) + $RobocopyArgs + "/ndl /TEE /bytes /Log:$ScanLog /nfl /L".Split(" ")
    $RoboArgs = @($SourceDir,$TargetDir,$FilesToCopy) + $RobocopyArgs + "/ndl /TEE /bytes /Log:$RoboLog /NC".Split(" ")

    # Launch Robocopy Processes
    write-verbose ("Robocopy Scan:`n" + ($ScanArgs -join " "))
    write-verbose ("Robocopy Full:`n" + ($RoboArgs -join " "))
    $ScanRun = start-process robocopy -PassThru -WindowStyle Hidden -ArgumentList $ScanArgs
    try
    {
        $RoboRun = start-process robocopy -PassThru -WindowStyle Hidden -ArgumentList $RoboArgs
        try
        {
            # Parse Robocopy "Scan" pass
            $ScanRun.WaitForExit()
            $LogData = get-content $ScanLog
            if ($ScanRun.ExitCode -ge 8)
            {
                $LogData|out-string|Write-Error
                throw "Robocopy $($ScanRun.ExitCode)"
            }
            $FileSize = [regex]::Match($LogData[-4],".+:\s+(\d+)\s+(\d+)").Groups[2].Value
            write-verbose ("Robocopy Bytes: $FileSize `n" +($LogData -join "`n"))
            #determine progress parameters
            $ProgressParms=@{}
            if ($ParentProgressID -ge 0) {
                $ProgressParms['ParentID']=$ParentProgressID
            }
            if ($ProgressID -ge 0) {
                $ProgressParms['ID']=$ProgressID
            } else {
                $ProgressParms['ID']=$RoboRun.Id
            }
            # Monitor Full RoboCopy
            while (!$RoboRun.HasExited)
            {
                $LogData = get-content $RoboLog
                $Files = $LogData -match "^\s*(\d+)\s+(\S+)"
                if ($null -ne $Files )
                {
                    $copied = ($Files[0..($Files.Length-2)] | ForEach-Object {$_.Split("`t")[-2]} | Measure-Object -sum).Sum
                    if ($LogData[-1] -match "(100|\d?\d\.\d)\%")
                    {
                        write-progress Copy -ParentID $ProgressParms['ID'] -percentComplete $LogData[-1].Trim("% `t") $LogData[-1]
                        $Copied += $Files[-1].Split("`t")[-2] /100 * ($LogData[-1].Trim("% `t"))
                    }
                    else
                    {
                        write-progress Copy -ParentID $ProgressParms['ID'] -Complete
                    }
                    write-progress ROBOCOPY  -PercentComplete ($Copied/$FileSize*100) $Files[-1].Split("`t")[-1] @ProgressParms
                }
            }
        } finally {
            if (!$RoboRun.HasExited) {Write-Warning "Terminating copy process with ID $($RoboRun.Id)..."; $RoboRun.Kill() ; }
            $RoboRun.WaitForExit()
            # Parse full RoboCopy pass results, and cleanup
            (get-content $RoboLog)[-11..-2] | out-string | Write-Verbose
            remove-item $RoboLog
            write-output ([PSCustomObject]@{ ExitCode = $RoboRun.ExitCode })

        }
    } finally {
        if (!$ScanRun.HasExited) {Write-Warning "Terminating scan process with ID $($ScanRun.Id)..."; $ScanRun.Kill() }
        $ScanRun.WaitForExit()

        remove-item $ScanLog
    }
}
person Justin    schedule 21.02.2020

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

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

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
}
person Dudi72    schedule 21.08.2017

Шон Кирни из Эй, сценарист! В блоге есть решение, которое, по моему мнению, работает очень хорошо.

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
person E-rich    schedule 08.01.2018
comment
Это сообщит о количестве файлов, скопированных в $Filelist, тогда как вопрос заключается в том, как сообщить о ходе копирования одного файла (т.е. о количестве байтов/блоков, скопированных на данный момент). Если бы этот код использовался для копирования одного большого файла, он не дал бы указания, как далеко продвинулась операция копирования в этом файле. Из тела вопроса: существуют решения для использования Write-Progress в сочетании с циклом для копирования многих файлов и отображения прогресса. Однако я не могу найти ничего, что показывало бы прогресс одного файла. - person Lance U. Matthews; 16.05.2018

У Тревора Салливана есть статья о том, как добавить команду Copy-ItemWithProgress в PowerShell на Robocopy.

person Wouter    schedule 20.04.2016

Ненавижу поднимать старую тему, но я нашел этот пост чрезвычайно полезным. После проведения тестов производительности фрагментов, проведенных stej и уточненных Грэмом Голдом, а также предложения BITS от Nacht, я пришел к выводу, что:

  1. Мне очень понравилась команда Грэма с оценками времени и показаниями скорости.
  2. Мне также очень понравилось значительное увеличение скорости при использовании BITS в качестве метода передачи.

Столкнувшись с выбором между двумя... Я обнаружил, что Start-BitsTransfer поддерживает асинхронный режим. Итак, вот результат моего слияния двух.

function Copy-File {
    # ref: https://stackoverflow.com/a/55527732/3626361
    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 '" + ($From.Split("\") | Select-Object -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
    }
}
person Shane    schedule 05.04.2019