Question

Est-il possible de copier un fichier vraiment grand (d'un serveur à l'autre) dans PowerShell et affiche sa progression?

Il existe des solutions pour utiliser Write-Progress en conjonction avec une boucle pour copier plusieurs fichiers et les progrès d'affichage. semblent cependant je ne peux pas trouver tout ce qui pourrait montrer les progrès d'un seul fichier.

Toutes les pensées?

Était-ce utile?

La solution

Je ne l'ai pas entendu parler de progrès avec Copy-Item. Si vous ne souhaitez pas utiliser un outil externe, vous pouvez expérimenter avec des flux. La taille du tampon varie, vous pouvez essayer différentes valeurs (de 2 Ko à 64 Ko).

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

Autres conseils

Il semble une bien meilleure solution à utiliser juste BitsTransfer, il semble venir OOTB sur la plupart des machines Windows avec PowerShell 2.0 ou supérieur.

Import-Module BitsTransfer
Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup"

Alternativly cette option utilise les fenêtres native barre de progression ...

$FOF_CREATEPROGRESSDLG = "&H0&"

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

$objFolder = $objShell.NameSpace($DestLocation) 

$objFolder.CopyHere($srcFile, $FOF_CREATEPROGRESSDLG)
cmd /c copy /z src dest

non PowerShell pure, mais exécutable dans PowerShell et il affiche la progression dans Percents

I modifié le code de STEJ (ce qui était super, juste ce qu'il me fallait!) Utiliser un tampon plus grand, [longue] pour des fichiers plus volumineux et classe System.Diagnostics.Stopwatch utilisé pour suivre le temps écoulé et le temps restant d'estimation.

rapports ajoutés également des taux de transfert pendant le transfert et la sortie du temps global écoulé et le taux global de transfert.

Utilisation 4Mo (4096 * 1024 octets) tampon pour obtenir mieux que la copie de débit natif Win7 du NAS à clé USB sur un ordinateur portable sur le wifi.

Sur la liste de choses à faire:

  • ajouter la gestion des erreurs (capture)
  • handle get-childitem liste des fichiers en entrée
  • barres de progression imbriquées lors de la copie de plusieurs fichiers (fichier x de y, si% total des données copiées etc)
  • paramètre d'entrée pour la taille de la mémoire tampon

Ne hésitez pas à utiliser / améliorer: -)

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();
    }
}

Pas que je sache. Je ne vous recommandons d'utiliser la copie article pour cela de toute façon. Je ne pense pas qu'il a été conçu pour être robuste comme robocopy.exe pour soutenir nouvelle tentative que vous voulez des copies de fichiers très volumineux sur le réseau.

Ce fichier récursives copie de la fonction et des répertoires récursive de chemin source vers le chemin de destination

Si le fichier existe déjà sur le chemin de destination, les copie uniquement avec des fichiers plus récents.

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
}

Sean Kearney du Hey, Scripting Guy! Blog a une solution que je trouve fonctionne assez bien.

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

Ensuite, pour l'utiliser:

Copy-WithProgress -Source $src -Destination $dest

Trevor Sullivan a une écriture sur la façon d'ajouter une commande appelée Copy-ItemWithProgress PowerShell sur Robocopy.

Je déteste être celui de tomber un vieux sujet, mais je trouve ce poste extrêmement utile. Après l'exécution des tests de performance sur les extraits par STEJ et il est raffinement par Graham Or, plus la suggestion BITS par Nacht, je decuded que:

  1. I vraiment a ajouté le commandement de Graham avec des estimations de temps et des lectures de vitesse.
  2. J'ai aussi vraiment a ajouté l'augmentation de vitesse significative de l'utilisation de BITS comme ma méthode de transfert.

Face à la décision entre les deux ... Je trouve que Start-BitsTransfer pris en charge en mode asynchrone. Donc, est le résultat ici de ma fusion des deux.

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
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top