التقدم أثناء نسخ الملفات الكبيرة (نسخ العنصر والكتابة التقدم؟)
-
19-09-2019 - |
سؤال
هل هناك أي طريقة لنسخ ملف كبير حقا (من خادم واحد إلى آخر) في PowerShell وعرض تقدمها؟
هناك حلول هناك لاستخدام تقدم الكتابة بالاقتران مع حلقات لنسخ العديد من الملفات وتقدم العرض. ومع ذلك، لا يمكنني العثور على أي شيء من شأنه إظهار تقدم ملف واحد.
أي أفكار؟
المحلول
أنا لم أسمع عن التقدم مع Copy-Item
. وبعد إذا كنت لا ترغب في استخدام أي أداة خارجية، فيمكنك تجربة التدفقات. يختلف حجم المخزن المؤقت، يمكنك تجربة قيم مختلفة (من 2 كيلوبايت إلى 64 كيلو بايت).
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
}
}
نصائح أخرى
يبدو وكأنه حلا أفضل بكثير لاستخدام Bitstransfer، يبدو أنه يأتي OOTB على معظم أجهزة Windows باستخدام PowerShell 2.0 أو أكبر.
Import-Module BitsTransfer
Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup"
بديل هذا الخيار يستخدم شريط التقدم المحرز الأصلي ...
$FOF_CREATEPROGRESSDLG = "&H0&"
$objShell = New-Object -ComObject "Shell.Application"
$objFolder = $objShell.NameSpace($DestLocation)
$objFolder.CopyHere($srcFile, $FOF_CREATEPROGRESSDLG)
cmd /c copy /z src dest
ليس PowerShell نقية، ولكن الملف القابل للتنفيذ في PowerShell ويعرض التقدم المحرز في النسب المئوية
عدلت الكود من Stej (الذي كان رائعا، فقط ما أحتاجه!) لاستخدام أكبر مخزن مؤقت، [طويل] للملفات الأكبر واستخدامها SYSTEM.DIAGNOSTICS.STOPWATCH فئة لتتبع الوقت المنقضي وقدر الوقت.
إضافة أيضا الإبلاغ عن معدل النقل أثناء النقل وإخراج الوقت المنقضي العام ومعدل النقل العام.
باستخدام المخزن المؤقت 4 ميغابايت (4096 * 1024 بايت) للحصول على أفضل من Win7 النسخ الإنتاجية الأصلية من NAS إلى عصا USB على الكمبيوتر المحمول عبر WiFi.
في قائمة المهام:
- إضافة معالجة خطأ (الصيد)
- التعامل مع قائمة ملف get-childitem كمدخلات
- أشرطة تقدم متداخلة عند نسخ ملفات متعددة (ملف X من y،٪ إذا تم نسخ إجمالي البيانات إلخ)
- المعلمة الإدخال لحجم المخزن المؤقت
لا تتردد في استخدام / تحسين :-)
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();
}
}
ليس هذا ما اخاف منه. لن أوصي باستخدام عنصر النسخ لهذا على أي حال. لا أعتقد أنه تم تصميمه ليكون قويا مثل Robocopy.exe لدعم إعادة المحاولة التي تريد أن ترغب في نسخ ملفات كبيرة للغاية عبر الشبكة.
تقوم هذه الوظيفة العودية بنسخ الملفات والدلائل بشكل متكرر من مسار المصدر إلى مسار الوجهة
إذا كان الملف موجودا بالفعل على مسار الوجهة، فسيتم نسخها فقط مع ملفات أحدث.
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
}
شون كيرني من مهلا، النص البرمجي! مقالات لديه حل وجدته بشكل جيد للغاية.
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++
}
}
ثم لاستخدامها:
Copy-WithProgress -Source $src -Destination $dest
يحتوي تريفور سوليفان على الكتابة حول كيفية إضافة أمر يسمى نسخة itemwithprogress. إلى Powershell على Robocopy.
أكره أن تكون الشخص الذي يصطدم بموضوع قديم، لكنني وجدت هذا المنشور مفيدا للغاية. بعد إجراء اختبارات الأداء على مقتطفات من قبل Stej والتحويل من قبل Graham Gold، بالإضافة إلى اقتراح BITS من قبل Nacht، لقد اسفت من ذلك:
- أنا حقا أمر يحب Graham بتقديرات زمنية وقراءات السرعة.
- أنا أيضا حقا أعجبني زيادة السرعة كبيرة في استخدام البتات كطريقة التحويل الخاصة بي.
واجهت القرار بين الاثنين ... لقد وجدت أن البيتسترانز فايت الوضع غير المتزامن. لذلك هنا هو نتيجة دمج بلدي اثنين.
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
}
}