programing

PowerShell의 사용자 지정 로보카피 진행률 표시줄

testmans 2023. 8. 18. 21:02
반응형

PowerShell의 사용자 지정 로보카피 진행률 표시줄

매일 서버에서 대량의 파일을 복사하는 PowerShell 스크립트에 관심이 있으며 다음과 같은 콘솔 진행률 표시줄을 구현하는 데 관심이 있습니다.

File copy status - XX% complete.

XX%새 줄 뒤에 새 줄 대신 같은 줄에서 업데이트합니다.저는 일단 로보카피로 결정했습니다.나는 현재 가지고 있습니다.

ROBOCOPY 'C:\Users\JMondy\Desktop\Sample1' 'C:\Users\JMondy\Desktop\Sample2' . /E /IS /NFL /NJH

다음 단계는 무엇입니까?

.Copy-WithProgress당신이 추구하는 것을 성취할 것입니다.당신이 로보카피를 사용한다고 특별히 말했기 때문에, 저는 로보카피 기능(적어도 일부)을 캡슐화하는 PowerShell 기능을 만들었습니다.

어떻게 작동하는지 보여드리겠습니다.는 또한 기능이 어떻게 작동하도록 설계되어 있는지를 보여주고 테스트 실행을 호출하는 유튜브 비디오를 녹화하여 게시했습니다.

기능은 다음 영역으로 나뉩니다.

  • 공통 로보카피 매개변수
  • 준비(로보카피 작업 크기가 계산되는 위치)
  • 복사(로보카피 작업이 시작되는 위치)
  • 진행률 표시줄(로보카피 진행률이 모니터링되는 위치)
  • 함수 출력(스크립트의 나머지 부분에 사용하기 위해 유용한 통계가 출력되는 경우)

함수에는 몇 가지 매개 변수가 있습니다.

  • 원본: 원본 디렉터리
  • 대상:대상 디렉터리
  • : 테스트를 위해 복사 속도를 인위적으로 늦추는 로보카피가 지원하는 "패킷 간 갭"(밀리초)
  • 보고서 간격:로보카피 진행률을 확인하는 간격(밀리초)

스크립트 하단(함수 정의 후)에는 이를 호출하는 방법에 대한 완전한 예가 나와 있습니다.모든 것이 가변적이기 때문에 컴퓨터에서 작동해야 합니다.5단계가 있습니다.

  1. 임의 원본 디렉터리 생성
  2. 대상 디렉터리 생성
  3. 에게 하세요.Copy-WithProgress를 수행
  4. 추가 소스 파일을 생성합니다(시간 경과에 따른 변경사항 에뮬레이트).
  5. 에게 하세요.Copy-WithProgress하며, 됩니다.

다음은 함수의 출력을 보여주는 스크린샷입니다.이 작업은 중단할 수 있습니다.-Verbose모든 디버깅 정보를 원하지 않는 경우 매개 변수 APSCustomObject됩니다.

  1. 복사된 바이트 수
  2. 복사된 파일 수

Copy-WithProgress PowerShell Function

다음은 PowerShell ISE의 PowerShell Progress Bar와 PowerShell 콘솔 호스트의 스크린샷입니다.

PowerShell Progress Bar (ISE)

PowerShell Progress Bar (Console Host)

코드는 다음과 같습니다.

function Copy-WithProgress {
    [CmdletBinding()]
    param (
            [Parameter(Mandatory = $true)]
            [string] $Source
        , [Parameter(Mandatory = $true)]
            [string] $Destination
        , [int] $Gap = 200
        , [int] $ReportGap = 2000
    )
    # Define regular expression that will gather number of bytes copied
    $RegexBytes = '(?<=\s+)\d+(?=\s+)';

    #region Robocopy params
    # MIR = Mirror mode
    # NP  = Don't show progress percentage in log
    # NC  = Don't log file classes (existing, new file, etc.)
    # BYTES = Show file sizes in bytes
    # NJH = Do not display robocopy job header (JH)
    # NJS = Do not display robocopy job summary (JS)
    # TEE = Display log in stdout AND in target log file
    $CommonRobocopyParams = '/MIR /NP /NDL /NC /BYTES /NJH /NJS';
    #endregion Robocopy params

    #region Robocopy Staging
    Write-Verbose -Message 'Analyzing robocopy job ...';
    $StagingLogPath = '{0}\temp\{1} robocopy staging.log' -f $env:windir, (Get-Date -Format 'yyyy-MM-dd HH-mm-ss');

    $StagingArgumentList = '"{0}" "{1}" /LOG:"{2}" /L {3}' -f $Source, $Destination, $StagingLogPath, $CommonRobocopyParams;
    Write-Verbose -Message ('Staging arguments: {0}' -f $StagingArgumentList);
    Start-Process -Wait -FilePath robocopy.exe -ArgumentList $StagingArgumentList -NoNewWindow;
    # Get the total number of files that will be copied
    $StagingContent = Get-Content -Path $StagingLogPath;
    $TotalFileCount = $StagingContent.Count - 1;

    # Get the total number of bytes to be copied
    [RegEx]::Matches(($StagingContent -join "`n"), $RegexBytes) | % { $BytesTotal = 0; } { $BytesTotal += $_.Value; };
    Write-Verbose -Message ('Total bytes to be copied: {0}' -f $BytesTotal);
    #endregion Robocopy Staging

    #region Start Robocopy
    # Begin the robocopy process
    $RobocopyLogPath = '{0}\temp\{1} robocopy.log' -f $env:windir, (Get-Date -Format 'yyyy-MM-dd HH-mm-ss');
    $ArgumentList = '"{0}" "{1}" /LOG:"{2}" /ipg:{3} {4}' -f $Source, $Destination, $RobocopyLogPath, $Gap, $CommonRobocopyParams;
    Write-Verbose -Message ('Beginning the robocopy process with arguments: {0}' -f $ArgumentList);
    $Robocopy = Start-Process -FilePath robocopy.exe -ArgumentList $ArgumentList -Verbose -PassThru -NoNewWindow;
    Start-Sleep -Milliseconds 100;
    #endregion Start Robocopy

    #region Progress bar loop
    while (!$Robocopy.HasExited) {
        Start-Sleep -Milliseconds $ReportGap;
        $BytesCopied = 0;
        $LogContent = Get-Content -Path $RobocopyLogPath;
        $BytesCopied = [Regex]::Matches($LogContent, $RegexBytes) | ForEach-Object -Process { $BytesCopied += $_.Value; } -End { $BytesCopied; };
        $CopiedFileCount = $LogContent.Count - 1;
        Write-Verbose -Message ('Bytes copied: {0}' -f $BytesCopied);
        Write-Verbose -Message ('Files copied: {0}' -f $LogContent.Count);
        $Percentage = 0;
        if ($BytesCopied -gt 0) {
           $Percentage = (($BytesCopied/$BytesTotal)*100)
        }
        Write-Progress -Activity Robocopy -Status ("Copied {0} of {1} files; Copied {2} of {3} bytes" -f $CopiedFileCount, $TotalFileCount, $BytesCopied, $BytesTotal) -PercentComplete $Percentage
    }
    #endregion Progress loop

    #region Function output
    [PSCustomObject]@{
        BytesCopied = $BytesCopied;
        FilesCopied = $CopiedFileCount;
    };
    #endregion Function output
}

# 1. TESTING: Generate a random, unique source directory, with some test files in it
$TestSource = '{0}\{1}' -f $env:temp, [Guid]::NewGuid().ToString();
$null = mkdir -Path $TestSource;
# 1a. TESTING: Create some test source files
1..20 | % -Process { Set-Content -Path $TestSource\$_.txt -Value ('A'*(Get-Random -Minimum 10 -Maximum 2100)); };

# 2. TESTING: Create a random, unique target directory
$TestTarget = '{0}\{1}' -f $env:temp, [Guid]::NewGuid().ToString();
$null = mkdir -Path $TestTarget;

# 3. Call the Copy-WithProgress function
Copy-WithProgress -Source $TestSource -Destination $TestTarget -Verbose;

# 4. Add some new files to the source directory
21..40 | % -Process { Set-Content -Path $TestSource\$_.txt -Value ('A'*(Get-Random -Minimum 950 -Maximum 1400)); };

# 5. Call the Copy-WithProgress function (again)
Copy-WithProgress -Source $TestSource -Destination $TestTarget -Verbose;

이러한 솔루션은 훌륭하지만 모든 파일을 쉽게 이동할 수 있는 빠르고 쉬운 방법은 다음과 같습니다.

robocopy <source> <destination> /MIR /NDL /NJH /NJS | %{$data = $_.Split([char]9); if("$($data[4])" -ne "") { $file = "$($data[4])"} ;Write-Progress "Percentage $($data[0])" -Activity "Robocopy" -CurrentOperation "$($file)"  -ErrorAction SilentlyContinue; }

꼭 로보카피를 사용해야 합니까?

그렇지 않은 경우 각 파일에 대해 이 스레드의 코드를 호출할 수 있습니다: 대용량 파일 복사 중 진행(Copy-Item & Write-Progress?)

또는 powershell에서 호출된 로보카피의 /L 스위치를 사용하여 로보카피가 복사한 파일 목록을 가져오고 각 루프를 사용하여 해당 복사 기능을 통해 각 파일을 실행합니다.

쓰기 진행률 명령을 중첩하여 "file x of y - XX% complete"를 보고할 수도 있습니다.

이와 같은 것이 작동해야 하고, 하위 디렉터리를 위한 약간의 작업이 필요하지만(gci 명령에 -recurse를 추가하는 것 이상의 것이 의심됩니다), 당신을 올바른 방향으로 이끌 것입니다.

참고: 전화로 쓰고 있습니다. 코드는 아직 테스트되지 않았습니다.

function Copy-File {
param( [string]$from, [string]$to)
$ffile = [io.file]::OpenRead($from)
$tofile = [io.file]::OpenWrite($to)
Write-Progress `
    -Activity ("Copying file " + $filecount + " of " + $files.count) `
    -status ($from.Split("\")|select -last 1) `
    -PercentComplete 0
try {
    $sw = [System.Diagnostics.Stopwatch]::StartNew();
    [byte[]]$buff = new-object byte[] 65536
    [long]$total = [long]$count = 0
    do {
        $count = $ffile.Read($buff, 0, $buff.Length)
        $tofile.Write($buff, 0, $count)
        $total += $count
        if ($total % 1mb -eq 0) {
            if([int]($total/$ffile.Length* 100) -gt 0)`
                {[int]$secsleft = ([int]$sw.Elapsed.Seconds/([int]($total/$ffile.Length* 100))*100)
                } else {
                [int]$secsleft = 0};
            Write-Progress `
                -Activity ([string]([int]($total/$ffile.Length* 100)) + "% Copying file")`
                -status ($from.Split("\")|select -last 1) `
                -PercentComplete ([int]($total/$ffile.Length* 100))`
                -SecondsRemaining $secsleft;
        }
    } while ($count -gt 0)
$sw.Stop();
$sw.Reset();
}
finally {
    $ffile.Close()
    $tofile.Close()
    }
}

$srcdir = "C:\Source;
$destdir = "C:\Dest";
[int]$filecount = 0;
$files = (Get-ChildItem $SrcDir | where-object {-not ($_.PSIsContainer)});
$files|foreach($_){
$filecount++
if ([system.io.file]::Exists($destdir+$_.name)){
                [system.io.file]::Delete($destdir+$_.name)}
                Copy-File -from $_.fullname -to ($destdir+$_.name)
};

개인적으로 저는 USB 스틱의 작은 복사본에는 이 코드를 사용하지만 PC 백업에는 파워셸 스크립트에 로보카피를 사용합니다.

다음은 로보카피의 기본 PowerShell GUI 버전입니다. (EXE 파일 없음)

누군가에게 도움이 되길 바랍니다.

enter image description here

https://gallery.technet.microsoft.com/PowerShell-Robocopy-GUI-08c9cacb

참고: PowerCopy GUI 도구와 Copy-WithProgress 막대를 결합할 수 있는 사람이 있습니까?

진행률 표시줄은 수백 개의 파일을 복사할 때를 제외하고는 모두 좋습니다. 진행률을 표시하면 작업 속도가 상당히 느려지는 경우도 있습니다.로보카피 도움말에서 /MT 플래그가 출력을 로그로 리디렉션하여 성능을 개선하도록 지시하는 이유 중 하나입니다.

이것은 제가 마침내 그러한 작업에 사용한 코드 스니펫입니다.

$fileName = 'test.txt'
$fromDir  = 'c:\'
$toDir    = 'd:\'

$title = $null
&robocopy "$fromDir" "$toDir" "$fileName" /z /mt /move /w:3 /r:10 /xo | %{
    $data = $_.Split("`t")
    if ($title -and $data[0] -match '\d+(?=%)') {
        Write-Progress $title -Status $data -PercentComplete $matches[0]
    }
    if($data[4]) {$title = $data[4]}
}
Write-Progress $title -complete

저는 Amrinder의 제안된 답변을 바탕으로 이것을 사용하게 되었습니다.

robocopy.exe $Source $Destination $PatternArg $MirrorArg /NDL /NJH /NJS | ForEach-Object -Process {
    $data = $_.Split([char]9);
    if (($data.Count -gt 4) -and ("$($data[4])" -ne ""))
    {
        $file = "$($data[4])"
        Write-Progress "Percentage $($data[0])" -Activity "Robocopy" -CurrentOperation "$($file)" -ErrorAction SilentlyContinue; 
    }
    else
    {
        Write-Progress "Percentage $($data[0])" -Activity "Robocopy" -CurrentOperation "$($file)"
    }
}
# Robocopy has a bitmask set of exit codes, so only complain about failures:
[int] $exitCode = $global:LastExitCode;
[int] $someCopyErrors = $exitCode -band 8;
[int] $seriousError = $exitCode -band 16;
if (($someCopyErrors -ne 0) -or ($seriousError -ne 0))
{
    Write-Error "ERROR: robocopy failed with a non-successful exit code: $exitCode"
    exit 1
}

안녕, 빌

이미 복사된 파일과 MB 수를 백분율 없이 보여주는 간단한 솔루션을 만들었습니다.이 경우 백분율이 필요하지 않으며 사용자는 진행 상황에 대한 메시지를 표시하기를 원할 뿐입니다.

저희 같은 경우에는.Copy-WithProgress수천 개의 작은 파일을 실제로 복사하기 전에 로그 파일을 만드는 데 시간이 걸렸습니다.

복사 진행 중에는 다음과 같이 표시됩니다.

완료되면 기본 요약이 표시됩니다.

코드는 다음과 같습니다.

$copiedFilesCount = 0
$copiedBytes = 0

robocopy $source $destination /ndl /bytes | ForEach-Object {
    $data = $_.Split([char]9)

    if ($data.Length -ge 4) { # check if message is from a file copy-process
        $copiedBytes += $data[3]
        $copiedFilesCount++
        
        Write-Progress -Activity "Robocopy" -Status "Copied $($copiedFilesCount.ToString("N0")) files ($(($copiedBytes / 1MB).ToString("N2")) MB)" -CurrentOperation $data[4]
    }
    elseif ($data -notmatch "\d+%") {
        Write-Output $_
    }
}

Write-Progress -Activity "Robocopy" -Completed

언급URL : https://stackoverflow.com/questions/13883404/custom-robocopy-progress-bar-in-powershell

반응형