
Чтобы убрать это с дороги:Я понимаю, что есть разные способы сделать это, например Get-ChildItem
:
Итак, у меня есть пользовательский класс, который я определяю, чтобы избавиться от некоторых накладных расходов, которые есть у командлетов PowerShell, а также некоторых классов .Net. Функция будет работать просто отлично локально, но как только я попытаюсь использовать ее как определение блока скрипта с Invoke-Command
удаленным компьютером, она просто зависнет;даже если я использую его против своего собственного компьютера. Существует процесс, который создан дляПлагин WinRMэто показано в диспетчере задач, но это все. Вот несколько рабочих примеров:
PS C:\Users\Abraham> Get-FolderSize -Path C:\Users\Abraham
143.98GB
PS C:\Users\Abraham> Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList C:\Users\Abraham
143.98GB
Как показано выше, это будет работать просто отлично и вернет сумму всех файлов. Затем, когда я передаю имя компьютера Invoke-Command
для удаленного выполнения - он просто зависает:
Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList C:\Users\Abraham -ComputerName $env:COMPUTERNAME
Само собой разумеется, что a PSSession
тоже не работает; это будет основной метод запуска функции -передавая его в открытыйPSSession
.
Мой вопрос: что, черт возьми, не так? Лол. Что-то происходит за кулисами, что не позволяет использоватьP/Вызовудаленно?
Вот фактическая функция:
Function Get-FolderSize ([parameter(Mandatory)][string]$Path) {
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace ProfileMethods
{
public class DirectorySum
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
private static extern bool FindClose(IntPtr hFindFile);
public long GetFolderSize(string path)
{
long size = 0;
List<string> dirList = new List<string>();
WIN32_FIND_DATA fileData;
IntPtr hFile = FindFirstFile(path + @"\*.*", out fileData);
if (hFile != IntPtr.Zero)
{
do
{
if (fileData.cFileName == "." || fileData.cFileName == "..")
{
continue;
}
string fullPath = path + @"\" + fileData.cFileName;
if ((fileData.dwFileAttributes & 0x10) == 0x10)
{
dirList.Add(fullPath);
}
else
{
size += ((long)fileData.nFileSizeHigh * (long)uint.MaxValue + (long)fileData.nFileSizeLow);
}
} while (FindNextFile(hFile, out fileData));
FindClose(hFile);
foreach (string dir in dirList)
{
size += GetFolderSize(dir);
}
}
return size;
}
}
}
"@
$program = [ProfileMethods.DirectorySum]::new()
switch ($program.GetFolderSize($Path))
{
{$_ -lt 1GB} { '{0}MB' -f [math]::Round($_/1MB,2); Continue }
{$_ -gt 1GB -and $_ -lt 1TB} { '{0}GB' -f [math]::Round($_/1GB,2); Continue }
{$_ -gt 1TB} { '{0}TB' -f [math]::Round($_/1TB,2); Continue }
}
}
РЕДАКТИРОВАТЬ:Обновлять- Итак, это работает для подпапок, но не для корневой папки. Пример:
$path = 'C:\Users\Abraham\Desktop' #works
Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList $path -ComputerName $env:COMPUTERNAME
...работает, а вот корневая папка C:\Users\Abraham
— нет.
Примечание:Передача UNC-пути к функции/методу будет работать.
решение1
Это всего лишь предположение, поскольку вы не пытались ничего расследовать, поэтому информации недостаточно, чтобы дать точный ответ.
но корневая папка C:\Users\Abraham — нет.
if ((fileData.dwFileAttributes & 0x10) == 0x10)
Современные системы Windows могут потребовать дополнительных проверок — вам нужно обратить внимание наточки повторной обработки,которые бывают разных типов (символические ссылки, соединения, точки монтирования, заполнители OneDrive...) и могут также иметь флаг «Directory» в дополнение к флагу «ReparsePoint» (который равен 0x400).
В частности, поскольку Vista переместила старый каталог «~\Local Settings» в ~\AppData\Local, для совместимости она размещает точку соединения каталогов в «~\AppData\Local\Application Data», которая указывает... обратно на тот же «~\AppData\Local».
Соединения немного более специфичны, чем символические ссылки, и могут иметь свои собственные ACL, поэтому обычно это соединение будет иметь Deny ACE, который запрещает вам это делать FindFirstFile(@"Application Data\*.*")
(т. е. оно разрешает только прямой доступ к известным путям). Но если ACL на нем когда-либо были сброшены, любая программа, пытающаяся перечислить содержимое AppData, в конечном итоге будет следовать бесконечному циклу, вечно спускаясь в одно и то же соединение (пока не исчерпает лимит длины пути).
FindFirstFile(path + @"\*.*", out fileData);
Помните, что имена файлов не обязательно должны содержать .
в себе . FindFirstFile() намеренно делает это (позволяя .*
сопоставлять пустую строку), чтобы помочь программам, которые все еще застряли в эпохе «8.3 filename.ext», но это не будет иметь места для программ, реализующих собственное расширение подстановочных знаков.