32

サーバーから毎日大量のファイルをコピーするPowerShellスクリプトに興味があり、次のようなコンソール内のプログレスバーを実装することに興味があります。

File copy status - XX% complete.

ここでXX%、改行の後に改行ではなく同じ行で更新されます。今のところRoboCopyを使用することにしました。私は現在持っています

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

次のステップは何ですか?

4

7 に答える 7

94

Copy-WithProgress私はあなたが求めているものを達成するというPowerShell関数を書きました。robocopyを使用していると具体的に述べたので、robocopy機能(少なくともその一部)をカプセル化するPowerShell関数を作成しました。

それがどのように機能するかをお見せしましょう。また、関数がどのように機能するように設計されているかを示し、テスト実行を呼び出すYouTubeビデオを録画して投稿しました。

関数は領域に分割されます:

  • 一般的なrobocopyパラメーター
  • ステージング(robocopyジョブサイズが計算される場所)
  • コピー(robocopyジョブが開始される場所)
  • プログレスバー(robocopyの進行状況が監視される場所)
  • 関数出力(スクリプトの残りの部分で使用するために、いくつかの有用な統計が出力されます)

関数にはいくつかのパラメーターがあります。

  • ソース:ソースディレクトリ
  • 宛先:宛先ディレクトリ
  • ギャップ:テストのために、コピーを人為的に遅くするrobocopyによってサポートされるミリ秒単位の「パケット間ギャップ」)
  • ReportGap:robocopyの進行状況を確認する間隔(ミリ秒単位)

スクリプトの下部(関数定義の後)には、スクリプトを呼び出す方法の完全な例があります。すべてが可変化されているので、それはあなたのコンピュータ上で動作するはずです。5つのステップがあります:

  1. ランダムなソースディレクトリを生成します
  2. 宛先ディレクトリを生成します
  3. Copy-WithProgress関数を呼び出す
  4. 追加のソースファイルをいくつか作成します(時間の経過に伴う変更をエミュレートするため)
  5. 関数を再度呼び出し、Copy-WithProgress変更のみが複製されていることを検証します

これは、関数の出力がどのように見えるかのスクリーンショットです。-Verboseすべてのデバッグ情報が必要ない場合は、パラメーターを省略できます。APSCustomObjectは、次のことを示す関数によって返されます。

  1. コピーされたバイト数
  2. コピーされたファイルの数

Copy-WithProgressPowerShell関数

これは、PowerShellISEの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;
于 2014-01-18T20:52:57.410 に答える
3

これらのソリューションは優れていますが、すべてのファイルの進行状況を簡単に把握するための迅速で簡単な方法は次のとおりです。

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; }
于 2014-08-15T22:15:07.827 に答える
2

絶対にrobocopyを使用する必要がありますか?

そうでない場合は、ファイルごとにこのスレッドのコードを呼び出すことができます。大きなファイルのコピー中の進行状況(コピー-アイテムと書き込み-進行状況?)

または、PowerShellから呼び出されたrobocopyの/ Lスイッチを使用して、robocopyがコピーしたファイルのリストを取得し、for-eachループを使用して各ファイルをそのコピー関数で実行します。

write-progressコマンドをネストして、「ファイル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スクリプトでrobocopyを使用します。

于 2013-02-11T23:11:49.847 に答える
2

これは、私が最終的にそのようなタスクに使用したコードスニペットです。

$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
于 2020-01-19T13:16:40.707 に答える
1

これがRoboCopyのネイティブPowerShellGUIバージョンです。(EXEファイルなし)

私はそれが誰かを助けることを願っています。

ここに画像の説明を入力してください

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

参考:PowerCopy GUIツールとCopy-WithProgressバーを組み合わせることができる人はいますか?

于 2015-04-06T14:42:36.410 に答える
1

プログレスバーは素晴らしく、何百ものファイルをコピーする場合を除いて、進行状況を表示すると操作が遅くなる場合があります。これが、robocopyヘルプが/ MTフラグに対して、パフォーマンスを向上させるために出力をログにリダイレクトするように指示している理由の1つです。

于 2015-07-22T13:14:33.617 に答える
0

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

ファイ、ビル

于 2015-04-06T14:04:01.660 に答える