编辑

编辑

我尝试使用 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] 的枚举进行排序,因此不必执行任何属性查找。

总体而言,您的代码执行的功能少了很多,执行功能少花费的时间也少了。但编写代码的时间更长,灵活性更低,而且重点更狭窄。这是一种权衡。

相关内容