ProgramingTip

대용량 파일 복사 중 진행률 (Copy-Item & Write-Progress?)

bestdevel 2020. 12. 31. 23:35
반응형

대용량 파일 복사 중 진행률 (Copy-Item & Write-Progress?)


PowerShell에서 정말 큰 파일 (한 서버에서 다른 서버로)을 복사하고 진행 상황을 표시하는 방법이 큰 파일입니까?

많은 파일을 복사하고 진행 상황을 표시하기 위해 루핑과 함께 Write-Progress를 사용하는 솔루션이 있습니다. 그러나 단일 파일의 진행 상황을 표시하는 것을 의미합니다.

이견있는 사람?


의 진행 상황에 대해 들어 본 적이 없습니다 Copy-Item. 외부 도구를 사용하지 않을 경우 스트림을 실험 할 수 있습니다. 버퍼의 크기는 다양하게 다른 값 (2kb에서 64kb까지)을 시도 할 수 있습니다.

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"

또는이 옵션은 기본 창 진행률 표시 줄을 사용합니다.

$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에서 순수한 실행 가능하며 진행률을 백분율로 표시합니다.


더 큰 버퍼를 사용하고 더 큰 파일에는 [long]을 사용하기 위해 stej의 코드를 수정하고 경과 시간을 추적하고 남은 시간을 추정하기 위해 System.Diagnostics.Stopwatch 클래스를 사용했습니다.

또한 전송 중 전송률보고 및 전체 경과 시간 및 전체 전송률 출력이 추가되었습니다.

4MB (4096 * 1024 바이트) 버퍼를 사용하여 Wi-Fi를 통해 NAS에서 노트북의 USB 스틱으로 복사하는 Win7 기본 처리량보다 향상됩니다.

할 일 목록 :

  • 오류 처리 추가 (catch)
  • 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();
    }
}

내가 아는 것은 아닙니다. 어쨌든 이것에 대해 복사 항목을 사용하지 않는 것이 좋습니다. 나는 그것이 네트워크를 통해 매우 큰 파일 복사본에 대해 원하는 재 시도를 지원하기 위해 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
}


안녕하세요, Scripting Guy! 의 Sean Kearney 블로그는 내가 약속이 꽤 잘 작동합니다.

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

Trevor Sullivan이 Robocopy의 PowerShell에 Copy-ItemWithProgress 라는 명령을 추가하는 방법에 대한 글을 작성했습니다 .


오래된 주제에 부딪히는 사람이되는 것을 싫어하지만이 게시물이 매우 유용하다는 것을 알았습니다. stej의 스 니펫에 대한 성능 테스트를 실행하고 Graham Gold의 개선과 Nacht의 BITS 제안에 대해 다음과 같이 설명했습니다.

  1. 나는 시간 예측과 속도 판독과 함께 Graham의 명령을 정말 좋아했습니다.
  2. 또한 전송 방법으로 BITS를 사용하는 속도가 크게 빨라지 는 것도 정말 마음에 들었습니다.

둘 사이의 결정에 직면했습니다 ... Start-BitsTransfer가 비동기 모드를 지원한다는 것을 알았습니다. 두 가지를 합친 결과입니다.

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

참조 URL : https://stackoverflow.com/questions/2434133/progress-during-large-file-copy-copy-item-write-progress

반응형