PowerShell中的自定义RoboCopy进度条

cod*_*ien 25 powershell

我对PowerShell脚本感兴趣,该脚本每天从服务器复制大量文件,我有兴趣实现一个控制台内部进度条,如

File copy status - XX% complete.

其中,XX%在同一条线上,而不是换行后换行更新.我决定暂时使用RoboCopy.我现在有

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

你下一步怎么做?

Tre*_*van 76

我编写了一个名为PowerShell的函数Copy-WithProgress,它将实现您的目标.由于您明确声明您使用的是robocopy,因此我构建了一个PowerShell函数,该函数封装了robocopy功能(至少部分内容).

请允许我告诉你它是如何工作的.我还录制并发布了一段YouTube视频,演示了该功能的设计方式,以及调用测试运行.

功能分为几个区域:

  • 常见的robocopy参数
  • 暂存(计算robocopy作业大小的位置)
  • 复制(启动robocopy作业的位置)
  • 进度条(监控robocopy进度)
  • 函数输出(输出一些有用的统计信息,用于脚本的其余部分)

该功能有几个参数.

  • 来源:源目录
  • 目的地:目的地目录
  • 差距:robocopy支持的"数据包间间隙"(以毫秒为单位),人工减慢副本,以进行测试)
  • ReportGap:检查robocopy进度的间隔(以毫秒为单位)

在脚本的底部(在函数定义之后),是一个如何调用它的完整示例.它应该可以在您的计算机上运行,​​因为所有内容都是可变的.有五个步骤:

  1. 生成随机源目录
  2. 生成目标目录
  3. 调用该Copy-WithProgress函数
  4. 创建一些其他源文件(以模拟随时间的变化)
  5. Copy-WithProgress再次调用该函数,并验证仅复制更改

以下是函数输出的截图.可以去掉的-Verbose参数,如果你不希望所有的调试信息.PSCustomObject函数返回A ,告诉您:

  1. 复制了多少字节
  2. 复制了多少个文件

Copy-WithProgress PowerShell功能

以下是PowerShell ISE和PowerShell控制台主机中PowerShell进度条的屏幕截图.

PowerShell进度条(ISE)

PowerShell进度条(控制台主机)

这是代码:

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;
Run Code Online (Sandbox Code Playgroud)

  • 我不得不说,这是一个经过深思熟虑的解决方案和帖子!我不知道OP,但我很震惊,很可能会自己使用这个:-) (7认同)
  • @GrahamGold不,别担心.我被吹走了.这是太棒了. (5认同)
  • @GrahamGold谢谢你,亲切的先生:)我很感激恭维.如有必要,请务必查看YouTube视频:http://www.youtube.com/watch?v = z9KeYa842rc (2认同)
  • 我还了解了 region/endregion 折叠,这对我来说是新的并且非常有用,并且你在我不使用的地方经常使用 `-f`,它会让生活更轻松。验证我为什么使用 SO,以获得帮助和学习:-) (2认同)
  • 我喜欢这段代码.我已将它合并到我自己的一些代码中.到目前为止,我发现的唯一问题是"$ FileCount = $ StagingContent.Count;".如果$ StagingLogContent为空(因为没有复制文件),.Count会抛出异常,所以我在我的上面添加了一个if条件,以便在获取.Count之前检查.Length -gt 0.我在if之前将$ FileCount默认为0. (2认同)

小智 7

这些解决方案很棒,但轻松获得所有文件的浮动进度的快速简便方法如下:

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; }
Run Code Online (Sandbox Code Playgroud)

  • 我找到了它,但这仅显示当前处理而不是所有进程 (3认同)