
為了解決這個問題:我知道有不同的方法可以解決這個問題,例如Get-ChildItem
。
因此,我定義了一個自訂類,以消除 PowerShell cmdlet 的一些開銷以及一些 .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
不言而喻,aPSSession
也不起作用;這將是運行該函數的主要方法 -將其傳遞給一個開放的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 佔位符...),除了「ReparsePoint」標誌(0x400)之外,還可能具有「Directory」標誌。
特別是,當Vista 將舊的“~\Local Settings”目錄移至~\AppData\Local 時,為了相容性,它在“~\AppData\Local\Application Data”處放置了一個目錄連接,該目錄指向……回到相同的「 ~\AppData\Local」。
連接點比符號連結稍微特殊一些,並且可以有自己的 ACL,因此通常該連接點將有一個拒絕 ACE 來阻止您執行操作FindFirstFile(@"Application Data\*.*")
(即,它只允許直接存取已知路徑)。但是,如果其上的 ACL 曾經被重置,則任何嘗試列舉 AppData 內容的程式都會最終陷入無限循環,永遠下降到同一個連接點(直到超出路徑長度限制)。
FindFirstFile(path + @"\*.*", out fileData);
.
請記住,檔案名稱中不需要包含。 FindFirstFile() 故意使這項工作(透過允許.*
匹配空字串)來幫助仍停留在「8.3 filename.ext」時代的程序,但對於實現自己的通配符擴展的程序來說,情況並非如此。