我尝试使用 PowerShell 做一件简单的事情,找出占用驱动器大部分空间的文件。我使用了ls
+ sort
,但……这花了我很长时间。
有时我会使用远程管理器,与 PowerShell 相比,它看起来更快、更稳定。
好的,它基于 .NET,但 .NET 并不慢。我希望看到一些轻量级且快速的东西!它是控制台!
另外,我希望IEnumerable
在 PowerShell 中有一些类似的东西可以立即看到结果。有可能实现吗?在等待结果时,这可能会有所帮助,因为有时我认为它只是挂了。
编辑
我正在做类似的事情
ls -Recurse -ErrorAction SilentlyContinue | sort -Property Size | select -First 10
我猜这可能需要几天的时间。
编辑
只是为了比较一下。
我花了大约 2 分钟来处理 C# 代码。当然,这并不理想,也没有处理所有文件,但它至少处理了 95% 以上。
void Main()
{
GetFilesSize(@"C:\").OrderByDescending(x => x).Take(10).ToList();
}
public IEnumerable<long> GetFilesSize(string directory)
{
var accessDenied = false;
var dirList = new string[0];
try
{
dirList = Directory.GetDirectories(directory);
}
catch{
accessDenied = true;
}
if(accessDenied) yield break;
foreach (var dir in dirList)
{
foreach (var size in GetFilesSize(dir))
{
yield return size;
}
}
foreach (var fileName in Directory.GetFiles(directory))
{
if(fileName.Length>=260) continue;
yield return new FileInfo(fileName).Length;
}
}
答案1
PowerShell 是一个用 .Net 编写的程序,但它在实际运行时利用了许多不同解释器和运行时的接口。它是一个 Shell,所以就像 BASH 一样,即使它是用 C 编写的,也没有提到在其中执行的二进制文件和脚本。可执行文件可能是 .Net 代码、VDM/CMD 命令、*nix shell 命令、VB/C/WSScript、WMI 调用、非托管 API 接口、jar 文件或其他任何东西。这些选择会影响在 shell 中运行的代码的性能,而不是编写 shell 的语言。
现在,听起来您在执行特定命令时遇到了困难。所以更好的问题是,为什么ls
在 PowerShell 中调用时排序很慢。当我们深入挖掘时,我们发现这ls
是一个别名对于“Get-ChildItem”,它返回一个包含 System.IO.DirectoryInfo 对象的对象数组。
PS C:\Windows\system32> $x=Get-ChildItem ./
PS C:\Windows\system32> $x.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS C:\Windows\system32> $x[1].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DirectoryInfo System.IO.FileSystemInfo
PS C:\Windows\system32>
您可以检索ls
结果,然后将其导入Sort-Object
称呼它的行为与 IEnumerable 的行为基本相同。
请注意,IEnumerable 不会对性能做任何事情。您可能会将其与 IQueryable 混淆,后者定义但直到最后一秒才执行查询,大概是在用过滤和排序操作修饰之后,就像 .Net 通过 LinQ to Objects 所做的那样。在这种情况下,由于 Get-ChildItem 不提供优化的查询引擎或索引数据源,因此您无法真正将现代数据库操作与目录列表进行比较。
所以,最终,尝试类似的方法:
ls ./ -recurse | Sort-Object Name -descending
对我来说,以 System32 为目标,处理和排序 54430 个文件大约需要 20 秒。
最后,请注意,当您尝试枚举您个人无权访问的目录时,您的性能会受到很大影响,因此请确保您没有递归到您无权去的地方,否则您将每次等待 2 秒以上。
希望有所帮助。
答案2
PowerShell 的设计初衷是方便,而非快速。这是一种权衡——它在后台工作,因此用户需要做的更少。工作越多,速度越慢。
您会发现,您的 PowerShell 代码只有一行,但其功能却比您的 C# 代码在 15 行中实现的还要多。
它可以做的更多——即使您没有使用它。
ls
在 Linux 上返回字符串,字符串简单且快速。您的 .Net 代码甚至不保留文件名,它只保留大小,而且数字又更小,因此甚至更快。
ls
在 PowerShell 中,返回 [FileInfo] 和 [DirectoryInfo] 对象 - 每个对象都必须创建,并且每个对象都必须查询文件以填充其他字段,如 CreationTime 和 LastWriteTime 以及 Extension 和 Length,并且时间字段必须创建 [DateTime] 对象。
对于每个文件来说,这都要慢得多。即使您不使用其他选项,也需要花费成本 - 您的 PowerShell 代码可能会发生变化,只需进行简单的更改即可获取 1 月份创建的前 10 个文件的大小,而无需其他 cmdlet 或工具,并且仍然是一行,C# 代码必须进行大量重写,查询创建时间,将创建时间和大小都带入排序,等等。
您无法立即看到结果的原因是您| sort
。这使其变得不可能。如果您立即开始输出结果,但找到的最后一个文件需要排在前面怎么办?那么输出将是错误的 - IEnumerable 对此无能为力,| sort
必须收集所有输入才能输出任何内容。您的排序速度更快,因为它对小东西进行排序
您的 .Net 代码可以更快地完成排序,因为它对 [long] 的枚举进行排序,因此不必执行任何属性查找。
总体而言,您的代码执行的功能少了很多,执行功能少花费的时间也少了。但编写代码的时间更长,灵活性更低,而且重点更狭窄。这是一种权衡。