Progrès pendant la copie de gros fichiers (Copie-Item & écriture-progrès?)

y a-t-il un moyen de copier un très gros fichier (d'un serveur à un autre) dans PowerShell et d'afficher sa progression?

il existe des solutions pour utiliser Write-Progress en conjonction avec looping pour copier de nombreux fichiers et afficher la progression. Cependant, je ne trouve rien qui montre l'évolution d'un seul fichier.

une idée?

47
demandé sur wonea 2010-03-12 19:29:05

9 réponses

Je n'ai pas entendu parler de progrès avec Copy-Item . Si vous ne voulez pas utiliser d'outil externe, vous pouvez expérimenter avec des flux. La taille du tampon varie, Vous pouvez essayer différentes valeurs (de 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
    }
}
39
répondu stej 2016-07-14 22:10:23

il semble comme une bien meilleure solution pour juste utiliser BitsTransfer, il semble venir OOTB sur la plupart des machines Windows avec PowerShell 2.0 ou plus.

Import-Module BitsTransfer
Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup"
61
répondu Nacht 2016-12-21 22:06:31

Alternativly cette option utilise la barre de progression de windows native...

$FOF_CREATEPROGRESSDLG = "&H0&"

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

$objFolder = $objShell.NameSpace($DestLocation) 

$objFolder.CopyHere($srcFile, $FOF_CREATEPROGRESSDLG)
19
répondu Chris M 2012-07-13 11:19:09

j'ai modifié le code de stej (ce qui était génial, juste ce dont j'avais besoin!) pour utiliser un tampon plus grand, [long] pour des fichiers plus gros et le système utilisé.Diagnostic.Classe du chronomètre pour suivre le temps écoulé et estimer le temps restant.

a également ajouté la déclaration du taux de transfert pendant le transfert et la sortie du temps total écoulé et du taux de transfert global.

utilisant un tampon de 4 Mo (4096*1024 octets) pour obtenir un meilleur débit que Win7 natif copie du NAS à USB bâton sur ordinateur portable sur wifi.

sur la liste des choses à faire:

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

N'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();
    }
}
12
répondu Graham Gold 2014-03-19 13:00:32
cmd /c copy /z src dest

pas pur PowerShell, mais exécutable en PowerShell et il affiche la progression en pourcentages

10
répondu Jirka Jr. 2018-04-04 08:51:51

pas que je sache. Je ne recommande pas l'utilisation de copy-item pour cela de toute façon. Je ne pense pas qu'il a été conçu pour être robuste comme robocopy.exe à l'appui de réessayer de qui vous voulez pour les très grandes des copies de fichiers sur le réseau.

9
répondu Keith Hill 2010-03-12 22:42:01

cette fonction récursive copie des fichiers et des répertoires de façon récursive du chemin source au chemin de destination

si le fichier existe déjà sur le chemin de destination, il ne les Copie qu'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
}
1
répondu Dudi72 2017-08-21 16:18:58

Sean Kearney du Hé, le gars des scripts! Blog a une solution que j'ai trouvé 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++
    }
}

puis l'utiliser:

Copy-WithProgress -Source $src -Destination $dest
1
répondu E-rich 2018-01-08 21:45:07

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

0
répondu Wouter 2018-04-04 08:52:04