InP*_*nic 4 powershell performance cmd
我可以统计一个文件夹和子文件夹中的所有文件,文件夹本身不计算在内。
(gci -Path *Fill_in_path_here* -Recurse -File | where Name -like "*STB*").Count
Run Code Online (Sandbox Code Playgroud)
但是,powershell 对于文件数量(最多 700k)来说太慢了。我读到 cmd 在执行此类任务时速度更快。
不幸的是,我根本不了解 cmd 代码。在上面的示例中,我计算STB了文件名中包含的所有文件。
这也是我想在 cmd 中做的事情。
任何帮助表示赞赏。
Theo基于直接使用 .NET ( [System.IO.Directory]::EnumerateFiles())的有用答案是最快的选择(在我的测试中;YMMV - 请参阅下面的基准代码[1])。
它限制在.NET框架(FullCLR)上- Windows PowerShell中建-是:
一个例外,当一个被抛出不可访问的目录中遇到的(由于缺乏权限)。您可以捕获异常,但不能继续枚举;也就是说,您不能健壮地枚举您可以访问的所有项目而忽略那些您不能访问的项目。
隐藏的项目总是包括在内。
通过递归枚举,始终遵循目录的符号链接/连接。
相比之下,跨平台的 .NET Core框架,自 v2.1 起- PowerShell Core在其上构建 -通过选项提供了绕过这些限制的方法EnumerationOptions- 请参阅此答案以获取示例。
请注意,您还可以通过相关[System.IO.DirectoryInfo]类型Get-ChildItem执行枚举,类似于- 返回丰富的对象,而不仅仅是路径字符串,允许进行多种处理;例如,要获取所有文件大小的数组(属性.Length,隐式应用于每个文件对象):
([System.IO.DirectoryInfo] $somePath).EnumerateFiles('*STB*', 'AllDirectories').Length
Run Code Online (Sandbox Code Playgroud)
解决这些限制并且仍然相当快的本机 PowerShell 解决方案是与参数一起使用。Get-ChildItem-Filter
(Get-ChildItem -LiteralPath $somePath -Filter *STB* -Recurse -File).Count
Run Code Online (Sandbox Code Playgroud)
默认排除隐藏项目;添加-Force以包含它们。
要忽略权限问题,请添加-ErrorAction SilentlyContinue或-ErrorAction Ignore; 这样做的好处SilentlyContinue是您可以稍后检查$Error集合以确定发生的具体错误,从而确保错误真正仅源于权限问题。
$env:USERPROFILE\Cookies.不幸的是,在Windows PowerShell 中,Get-ChildItem -Recurse 总是遵循符号链接/连接到目录;更理智,PowerShell的核心默认情况下不不,并提供选入通过-FollowSymlink。
与[System.IO.DirectoryInfo]基于 -based 的解决方案一样,Get-ChildItem输出描述每个枚举文件系统项的丰富对象( [System.IO.FileInfo]/ [System.IO.DirectoryInfo]),允许进行多种处理。
请注意,虽然您也可以将通配符参数传递给-Path(隐含的第一个位置参数)和-Include(如TobyU 的回答),但-Filter由于在源(文件系统驱动程序)进行过滤,它只能提供显着的速度改进,以便 PowerShell接收已经过滤的结果;相比之下,-Path/-Include必须首先枚举所有内容,然后匹配通配符模式。[2]
注意事项重新-Filter使用:
*[0-9])并且它有遗留的怪癖 - 请参阅此答案。-Include支持多个(作为数组)。也就是说,-Filter处理通配符的方式与cmd.exe's相同dir。
最后,为了完整起见,您可以根据在 PowerShell 中使用的命令改编MC ND 的有用答案,这简化了问题:cmd.exedir
(cmd /c dir /s /b /a-d "$somePath/*STB*").Count
Run Code Online (Sandbox Code Playgroud)
PowerShell 将外部程序的 stdout 输出捕获为行数组,您可以使用.Count(or .Length) 属性简单地查询其元素计数。
也就是说,这可能会或可能不会比 PowerShell 自己的 更快Get-ChildItem -Filter,具体取决于过滤方案;还请注意,dir /s只能返回路径字符串,而Get-ChildItem返回可以查询其属性的丰富对象。
注意事项重新dir使用:
/a-d排除目录,即只报告文件,但也包括隐藏文件,dir默认情况下不这样做。
dir /s 在递归枚举过程中也总是下降到隐藏目录中;一个/a(基于属性的)滤波器仅施加到叶枚举的项(仅到文件在这种情况下)。
dir /s 总是遵循符号链接/连接到其他目录(假设它具有必要的权限 - 见下一点)。
dir /s 如果由于缺乏权限而无法枚举目录的内容,则悄悄地忽略目录或目录的符号链接/连接 - 虽然这在上述隐藏系统连接的特定情况下很有帮助(您可以使用 找到它们cmd /c dir C:\ /s /ashl),但它可能会导致您错过您确实想要枚举的目录的内容,但不能因为真正缺乏权限,因为dir /s不会表明此类内容甚至可能存在(如果您直接定位一个无法访问的目录,您会收到一些误导性的File Not Found错误消息,并且退出代码设置为1)。
性能对比:
以下测试比较了没有过滤的纯枚举性能,为简单起见,使用假设在所有系统上都存在的相当大的目录树c:\windows\winsxs;也就是说,很容易调整测试以比较过滤性能。
测试是从 PowerShell 运行的,这意味着通过创建子进程cmd.exe来调用会引入一些开销dir /s,尽管 (a) 开销应该相对较低 (b) 更重要的是停留在 PowerShell 领域非常值得,因为与cmd.exe.
测试使用 function Time-Command,它可以从这个 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 """"' }
Run Code Online (Sandbox Code Playgroud)
在 Microsoft Windows 10 Pro(64 位;版本 1803,操作系统版本:17134.523)上使用 Windows PowerShell v5.1.17134.407 的单核 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
Run Code Online (Sandbox Code Playgroud)
有趣的是,在 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
Run Code Online (Sandbox Code Playgroud)
[1][System.IO.Directory]::EnumerateFiles()本质上并且无疑比Get-ChildItem解决方案更快。在我的测试(请参见“性能比较:”以上),[System.IO.Directory]::EnumerateFiles()拍出来cmd /c dir /s为好,稍在Windows PowerShell中,并明确因此在PowerShell核心,但其他报告不同的结果。也就是说,找到整体最快的解决方案并不是唯一的考虑因素,特别是如果需要的不仅仅是计算文件并且枚举需要健壮。这个答案讨论了各种解决方案的权衡。
[2] 事实上,由于 Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.4 的低效率实现,使用-Path和-Include实际上比使用未过滤的和使用带有 的附加管道段慢,如在OP - 请参阅此 GitHub 问题。Get-ChildItem... | Where-Object Name -like *STB*
| 归档时间: |
|
| 查看次数: |
4963 次 |
| 最近记录: |