有没有办法在PowerShell中复制一个非常大的文件(从一个服务器到另一个服务器)并显示进度?
有一些解决方案可以使用Write-Progress与循环结合使用来复制多个文件并显示进度。 但是,我似乎找不到任何可以显示单个文件进度的东西。
有什么想法吗?
有没有办法在PowerShell中复制一个非常大的文件(从一个服务器到另一个服务器)并显示进度?
有一些解决方案可以使用Write-Progress与循环结合使用来复制多个文件并显示进度。 但是,我似乎找不到任何可以显示单个文件进度的东西。
有什么想法吗?
这是一篇旧帖子,但我认为它可能会对其他人有所帮助。
使用FileStream的解决方案很优雅且有效,但速度非常慢。
我认为使用其他程序,如robocopy.exe,会削弱powershell的目的。
这甚至是Monad宣言中的动机之一。
因此,我打开了Microsoft.PowerShell.Management中的Copy-Item cmdlet,并在最后调用了kernel32.dll中的CopyFileEx。
在CopyFileEx签名中,有一个参数接受回调以提供进度信息。
在pinvoke.net上,有一个很好的示例,展示了如何将此函数和回调封送到委托中。
我稍微修改了一下,以便我们可以从PS脚本本身提供委托。
相信我,当我说这个时,我并没有期望它会工作:D(我真的从椅子上跳了下来)。
而且它的速度要快得多。
以下是代码:
function Copy-File {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0)]
[string]$Path,
[Parameter(Mandatory, Position = 1)]
[string]$Destination
)
$signature = @'
namespace Utilities {
using System;
using System.Runtime.InteropServices;
public class FileSystem {
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CopyFileEx(
string lpExistingFileName,
string lpNewFileName,
CopyProgressRoutine lpProgressRoutine,
IntPtr lpData,
ref Int32 pbCancel,
CopyFileFlags dwCopyFlags
);
delegate CopyProgressResult CopyProgressRoutine(
long TotalFileSize,
long TotalBytesTransferred,
long StreamSize,
long StreamBytesTransferred,
uint dwStreamNumber,
CopyProgressCallbackReason dwCallbackReason,
IntPtr hSourceFile,
IntPtr hDestinationFile,
IntPtr lpData);
int pbCancel;
public enum CopyProgressResult : uint
{
PROGRESS_CONTINUE = 0,
PROGRESS_CANCEL = 1,
PROGRESS_STOP = 2,
PROGRESS_QUIET = 3
}
public enum CopyProgressCallbackReason : uint
{
CALLBACK_CHUNK_FINISHED = 0x00000000,
CALLBACK_STREAM_SWITCH = 0x00000001
}
[Flags]
enum CopyFileFlags : uint
{
COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
COPY_FILE_RESTARTABLE = 0x00000002,
COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
}
public void CopyWithProgress(string oldFile, string newFile, Func<long, long, long, long, uint, CopyProgressCallbackReason, System.IntPtr, System.IntPtr, System.IntPtr, CopyProgressResult> callback)
{
CopyFileEx(oldFile, newFile, new CopyProgressRoutine(callback), IntPtr.Zero, ref pbCancel, CopyFileFlags.COPY_FILE_RESTARTABLE);
}
}
}
'@
Add-Type -TypeDefinition $signature
[Func[long, long, long, long, System.UInt32, Utilities.FileSystem+CopyProgressCallbackReason, System.IntPtr, System.IntPtr, System.IntPtr, Utilities.FileSystem+CopyProgressResult]]$copyProgressDelegate = {
param($total, $transfered, $streamSize, $streamByteTrans, $dwStreamNumber, $reason, $hSourceFile, $hDestinationFile, $lpData)
Write-Progress -Activity "Copying file" -Status "$Path ~> $Destination. $([Math]::Round(($transfered/1KB), 2))KB/$([Math]::Round(($total/1KB), 2))KB." -PercentComplete (($transfered / $total) * 100)
}
$fileName = [System.IO.Path]::GetFileName($Path)
$destFileName = [System.IO.Path]::GetFileName($Destination)
if ([string]::IsNullOrEmpty($destFileName) -or $destFileName -notlike '*.*') {
if ($Destination.EndsWith('\')) {
$destFullName = "$Destination$fileName"
}
else {
$destFullName = "$Destination\$fileName"
}
}
$wrapper = New-Object Utilities.FileSystem
$wrapper.CopyWithProgress($Path, $destFullName, $copyProgressDelegate)
}
希望能有所帮助。
愉快地编写脚本吧!
更新:
同样的事情,但使用CopyFile2。
try {
Add-Type -TypeDefinition @'
namespace Utilities {
using System;
using System.Text;
using System.Runtime.InteropServices;
public delegate COPYFILE2_MESSAGE_ACTION CopyFile2ProgressRoutine(
[In] COPYFILE2_MESSAGE pMessage,
[In, Optional] IntPtr pvCallbackContext
);
[Flags]
public enum CopyFlags : uint {
COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
COPY_FILE_RESTARTABLE = 0x00000002,
COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008,
COPY_FILE_COPY_SYMLINK = 0x00000800,
COPY_FILE_NO_BUFFERING = 0x00001000,
COPY_FILE_REQUEST_SECURITY_PRIVILEGES = 0x00002000,
COPY_FILE_RESUME_FROM_PAUSE = 0x00004000,
COPY_FILE_NO_OFFLOAD = 0x00040000,
COPY_FILE_REQUEST_COMPRESSED_TRAFFIC = 0x10000000
}
public enum COPYFILE2_MESSAGE_ACTION : uint {
COPYFILE2_PROGRESS_CONTINUE,
COPYFILE2_PROGRESS_CANCEL,
COPYFILE2_PROGRESS_STOP,
COPYFILE2_PROGRESS_QUIET,
COPYFILE2_PROGRESS_PAUSE
}
public enum COPYFILE2_MESSAGE_TYPE : uint {
COPYFILE2_CALLBACK_NONE,
COPYFILE2_CALLBACK_CHUNK_STARTED,
COPYFILE2_CALLBACK_CHUNK_FINISHED,
COPYFILE2_CALLBACK_STREAM_STARTED,
COPYFILE2_CALLBACK_STREAM_FINISHED,
COPYFILE2_CALLBACK_POLL_CONTINUE,
COPYFILE2_CALLBACK_ERROR,
COPYFILE2_CALLBACK_MAX
}
public enum COPYFILE2_COPY_PHASE : uint {
COPYFILE2_PHASE_NONE,
COPYFILE2_PHASE_PREPARE_SOURCE,
COPYFILE2_PHASE_PREPARE_DEST,
COPYFILE2_PHASE_READ_SOURCE,
COPYFILE2_PHASE_WRITE_DESTINATION,
COPYFILE2_PHASE_SERVER_COPY,
COPYFILE2_PHASE_NAMEGRAFT_COPY,
COPYFILE2_PHASE_MAX
}
[StructLayout(LayoutKind.Sequential)]
public struct ULARGE_INTEGER {
public Int64 QuadPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct _ChunkStarted {
public uint dwStreamNumber;
public uint dwReserved;
public IntPtr hSourceFile;
public IntPtr hDestinationFile;
public ULARGE_INTEGER uliChunkNumber;
public ULARGE_INTEGER uliChunkSize;
public ULARGE_INTEGER uliStreamSize;
public ULARGE_INTEGER uliTotalFileSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct _ChunkFinished {
public uint dwStreamNumber;
public uint dwFlags;
public IntPtr hSourceFile;
public IntPtr hDestinationFile;
public ULARGE_INTEGER uliChunkNumber;
public ULARGE_INTEGER uliChunkSize;
public ULARGE_INTEGER uliStreamSize;
public ULARGE_INTEGER uliStreamBytesTransferred;
public ULARGE_INTEGER uliTotalFileSize;
public ULARGE_INTEGER uliTotalBytesTransferred;
}
[StructLayout(LayoutKind.Sequential)]
public struct _StreamStarted {
public uint dwStreamNumber;
public uint dwReserved;
public IntPtr hSourceFile;
public IntPtr hDestinationFile;
public ULARGE_INTEGER uliStreamSize;
public ULARGE_INTEGER uliTotalFileSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct _StreamFinished {
public uint dwStreamNumber;
public uint dwReserved;
public IntPtr hSourceFile;
public IntPtr hDestinationFile;
public ULARGE_INTEGER uliStreamSize;
public ULARGE_INTEGER uliStreamBytesTransferred;
public ULARGE_INTEGER uliTotalFileSize;
public ULARGE_INTEGER uliTotalBytesTransferred;
}
[StructLayout(LayoutKind.Sequential)]
public struct _PollContinue {
public uint dwReserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct _Error {
COPYFILE2_COPY_PHASE CopyPhase;
uint dwStreamNumber;
IntPtr hrFailure;
uint dwReserved;
ULARGE_INTEGER uliChunkNumber;
ULARGE_INTEGER uliStreamSize;
ULARGE_INTEGER uliStreamBytesTransferred;
ULARGE_INTEGER uliTotalFileSize;
ULARGE_INTEGER uliTotalBytesTransferred;
}
[StructLayout(LayoutKind.Explicit)]
public struct COPYFILE2_MESSAGE {
[FieldOffset(0)]
public COPYFILE2_MESSAGE_TYPE Type;
[FieldOffset(1)]
public uint dwPadding;
[FieldOffset(2)]
public _ChunkStarted ChunkStarted;
[FieldOffset(2)]
public _ChunkFinished ChunkFinished;
[FieldOffset(2)]
public _StreamStarted StreamStarted;
[FieldOffset(2)]
public _StreamFinished StreamFinished;
[FieldOffset(2)]
public _PollContinue PollContinue;
[FieldOffset(2)]
public _Error Error;
}
[StructLayout(LayoutKind.Sequential)]
public struct COPYFILE2_EXTENDED_PARAMETERS {
public uint dwSize;
public CopyFlags dwCopyFlags;
public bool pfCancel;
public CopyFile2ProgressRoutine pProgressRoutine;
public IntPtr pvCallbackContext;
}
public class FileSystem {
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint CopyFile2(
string pwszExistingFileName,
string pwszNewFileName,
COPYFILE2_EXTENDED_PARAMETERS pExtendedParameters
);
public static void CopyFileEx(string filePath, string destination, Func<COPYFILE2_MESSAGE, IntPtr, COPYFILE2_MESSAGE_ACTION> callback) {
COPYFILE2_EXTENDED_PARAMETERS extParams = new();
extParams.dwSize = Convert.ToUInt32(Marshal.SizeOf(extParams));
extParams.dwCopyFlags = CopyFlags.COPY_FILE_NO_BUFFERING | CopyFlags.COPY_FILE_COPY_SYMLINK;
extParams.pProgressRoutine = new CopyFile2ProgressRoutine(callback);
extParams.pvCallbackContext = IntPtr.Zero;
uint result = CopyFile2(filePath, destination, extParams);
if (result != 0)
throw new SystemException(result.ToString());
}
}
}
'@
}
catch { }
[Func[
Utilities.COPYFILE2_MESSAGE,
IntPtr,
Utilities.COPYFILE2_MESSAGE_ACTION
]]$delegate = {
param([Utilities.COPYFILE2_MESSAGE]$message, $extArgs, $result)
if ($message.Type -eq [Utilities.COPYFILE2_MESSAGE_TYPE]::COPYFILE2_CALLBACK_CHUNK_FINISHED) {
Write-Progress -Activity 'Copying file.' -Status 'Copying...' -PercentComplete (($message.ChunkFinished.uliTotalFileSize.QuadPart / $message.ChunkFinished.uliStreamBytesTransferred.QuadPart) * 100)
}
}
if (Test-Path -Path C:\CopyFile2TestDestination -PathType Container) { [void](mkdir C:\CopyFile2TestDestination) }
[Utilities.FileSystem]::CopyFileEx('C:\superTest.dat', 'C:\CopyFile2TestDestination\superTestCopy.dat', $delegate)