gpt4 book ai didi

performance - 有效计数目录和子文件夹中具有特定名称的文件

转载 作者:行者123 更新时间:2023-12-02 07:33:20 31 4
gpt4 key购买 nike

我可以计算一个文件夹和子文件夹中的所有文件,不计算文件夹本身。

(gci -Path *Fill_in_path_here* -Recurse -File | where Name -like "*STB*").Count

但是,对于文件数量(最多700k),powershell太慢。我读到cmd在执行此类任务时速度更快。

不幸的是,我一点都不了解cmd代码。在上面的示例中,我正在计算文件名中带有 STB的所有文件。

这也是我想在cmd中执行的操作。

任何帮助表示赞赏。

最佳答案

基于Theo's helpful answer直接使用.NET的([System.IO.Directory]::EnumerateFiles())是最快的选择(在我的测试中; YMMV-请参见下面的基准代码[1])。

它在 .NET Framework(FullCLR)中的限制上-Windows PowerShell 生成于其上-是:

  • 遇到无法访问的目录(由于缺少权限)时,将引发异​​常。您可以捕获异常,但是无法继续枚举;即,您不能稳健地枚举可以访问的所有项目,而忽略那些您不能访问的项目。
  • 总是包含隐藏的项目。
  • 通过递归枚举,始终会遵循目录的符号链接(symbolic link)/结点。

  • 相比之下,自从v2.1 (基于 的PowerShell Core 构建于v2.1版本)以来, 跨平台.NET Core框架通过 EnumerationOptions 选项提供了围绕限制的 方式,请参见this answer

    注意,您还可以通过相关的 [System.IO.DirectoryInfo] 类型执行枚举,与Get-ChildItem相似,该枚举返回丰富的对象而不是仅是路径字符串,从而为通用处理提供了很多资源;例如,获取所有文件大小的数组(属性.Length,隐式应用于每个文件对象):
    ([System.IO.DirectoryInfo] $somePath).EnumerateFiles('*STB*', 'AllDirectories').Length

    解决这些限制并且仍然相当快的本机PowerShell解决方案是将Get-ChildItem -Filter参数一起使用。
    (Get-ChildItem -LiteralPath $somePath -Filter *STB* -Recurse -File).Count
  • 默认情况下排除隐藏的项目;添加-Force以包括它们。
  • 要忽略权限问题,请添加-ErrorAction SilentlyContinue-ErrorAction IgnoreSilentlyContinue的优点在于,您以后可以检查$Error集合以确定发生的特定错误,以确保这些错误确实仅源于权限问题。
  • 请注意,与Windows PowerShell不同,PowerShell Core有助于忽略无法枚举hidden system junctions that exist for pre-Vista compatibility only的内容(例如$env:USERPROFILE\Cookies)的情况。
  • 在Windows PowerShell中,不幸的是,Get-ChildItem -Recurse始终遵循符号链接(symbolic link)/结点的目录。更明智的是,PowerShell Core默认情况下不提供,并通过-FollowSymlink提供选择加入。
  • 与基于[System.IO.DirectoryInfo]的解决方案一样,Get-ChildItem输出丰富的对象( [System.IO.FileInfo] / [System.IO.DirectoryInfo] ),这些对象描述每个枚举的文件系统项,从而实现了通用处理。

  • 请注意,虽然您还可以将通配符传递给-Path(隐含的第一个位置参数)和-Include(如TobyU's answer一样),但只是提供支持的-Filter由于在源(文件系统驱动程序)处进行了过滤,因此速度显着提高,因此PowerShell仅接收已过滤的结果;相反,对于-Path / -Include必须首先枚举所有内容,然后在之前与通配符模式匹配。[2]

    请注意-Filter使用:
  • 它的通配符语言与PowerShell的通配符语言不同。值得注意的是,它不支持字符集/范围(例如*[0-9]),并且具有遗留的怪癖-请参见this answer
  • 它仅支持单个通配符模式,而-Include支持多个(作为数组)。

  • 就是说,-Filter处理通配符的方式与cmd.exedir相同。

    最后,为了完整起见,您可以根据cmd.exedir命令修改MC ND's helpful answer,以在PowerShell中使用,从而简化了事情:
    (cmd /c dir /s /b /a-d "$somePath/*STB*").Count

    PowerShell将外部程序的stdout输出捕获为行数组,您可以简单地使用.Count(或.Length)属性查询其元素计数。

    也就是说,这可能会或可能不会快于PowerShell自己的Get-ChildItem -Filter,具体取决于过滤方案;还要注意dir /s只能返回路径字符串,而Get-ChildItem返回可以查询其属性的丰富对象。

    请注意dir使用:
  • /a-d排除目录,即仅报告文件,但还包括隐藏文件,dir默认情况下不执行。
  • dir /s在递归枚举过程中也始终会进入隐藏目录; /a(基于属性)过滤器仅应用于枚举的叶项(在这种情况下仅应用于文件)。
  • dir /s始终遵循与其他目录的符号链接(symbolic link)/连接(假设它具有必要的权限-请参阅下一点)。
  • 如果由于缺少权限而无法枚举目录内容,
  • dir /s会静默忽略目录或目录的符号链接(symbolic link)/联结-尽管在上述隐藏系统联结的特定情况下这很有用(您可以使用cmd /c dir C:\ /s /ashl找到它们)您会错过您想枚举的目录的内容,但不能真正缺少权限,因为dir /s不会表明此类内容甚至可能存在(如果您直接针对无法访问的目录,则会产生误导性File Not Found错误消息,并且退出代码设置为1)。


  • 性能比较:
  • 为了简单起见,下面的测试使用假定在所有系统上都存在的可观目录树,比较没有过滤的纯枚举性能c:\windows\winsxs;也就是说,很容易调整测试以比较过滤性能。
  • 测试是从PowerShell运行的,这意味着通过创建cmd.exe的子进程来调用dir /s会引入一些开销,尽管(a)开销应该相对较低,并且(b)较大的一点是保持在与cmd.exe相比,PowerShell Realm 具有超强的功能,因此非常值得。
  • 测试使用Time-Command函数,可以从this Gist下载该函数,默认情况下平均运行10次。

  • # Warm up the filesystem cache for the target dir.,
    # both from PowerShell and cmd.exe, to be safe.
    gci 'c:\windows\winsxs' -rec >$null; cmd /c dir /s 'c:\windows\winsxs' >$null

    Time-Command `
    { @([System.IO.Directory]::EnumerateFiles('c:\windows\winsxs', '*', 'AllDirectories')).Count },
    { (Get-ChildItem -Force -Recurse -File 'c:\windows\winsxs').Count },
    { (cmd /c dir /s /b /a-d 'c:\windows\winsxs').Count },
    { cmd /c 'dir /s /b /a-d c:\windows\winsxs | find /c /v """"' }

    在装有Microsoft Windows 10 Pro的Windows PowerShell v5.1.17134.407(64位;版本1803,操作系统内部版本:17134.523)的单核VMWare Fusion VM上,我得到以下计时,从最快到最慢(向右滚动到请参阅Factor列以显示相对性能):
    Command                                                                                    Secs (10-run avg.) TimeSpan         Factor
    ------- ------------------ -------- ------
    @([System.IO.Directory]::EnumerateFiles('c:\windows\winsxs', '*', 'AllDirectories')).Count 11.016 00:00:11.0158660 1.00
    (cmd /c dir /s /b /a-d 'c:\windows\winsxs').Count 15.128 00:00:15.1277635 1.37
    cmd /c 'dir /s /b /a-d c:\windows\winsxs | find /c /v """"' 16.334 00:00:16.3343607 1.48
    (Get-ChildItem -Force -Recurse -File 'c:\windows\winsxs').Count 24.525 00:00:24.5254979 2.23

    有趣的是,PowerShell Core中的[System.IO.Directory]::EnumerateFiles()Get-ChildItem解决方案都明显更快,它在.NET Core之上运行(从PowerShell Core 6.2.0-preview.4,.NET Core 2.1开始):
    Command                                                                                    Secs (10-run avg.) TimeSpan         Factor
    ------- ------------------ -------- ------
    @([System.IO.Directory]::EnumerateFiles('c:\windows\winsxs', '*', 'AllDirectories')).Count 5.094 00:00:05.0940364 1.00
    (cmd /c dir /s /b /a-d 'c:\windows\winsxs').Count 12.961 00:00:12.9613440 2.54
    cmd /c 'dir /s /b /a-d c:\windows\winsxs | find /c /v """"' 14.999 00:00:14.9992965 2.94
    (Get-ChildItem -Force -Recurse -File 'c:\windows\winsxs').Count 16.736 00:00:16.7357536 3.29

    [1] [System.IO.Directory]::EnumerateFiles()无疑比Get-ChildItem解决方案要快。在我的测试中(请参阅上面的“性能比较:”部分),[System.IO.Directory]::EnumerateFiles()也击败了cmd /c dir /s,在Windows PowerShell中稍胜一筹,在PowerShell Core中显然如此,但在others report different findings之下。就是说,寻找整体最快的解决方案不是唯一的考虑因素,尤其是如果需要的不仅仅是计数文件,并且枚举必须可靠的话。该答案讨论了各种解决方案的权衡。

    [2]实际上,由于Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.4的实现效率低下,使用-Path-Include实际上比使用未过滤的Get-ChildItem慢,而使用带有... | Where-Object Name -like *STB*的附加管道段要慢,如OP中所示,请参见this GitHub issue

    关于performance - 有效计数目录和子文件夹中具有特定名称的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54215968/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com