powershell脚本太慢(文件枚举)

powershell脚本太慢(文件枚举),powershell,Powershell,我有一个简单的PowerShell脚本,它在目录树中运行,并以JSON格式列出文件 每个条目的格式如下: {id:filename,大小:bytes} 适用于短列表,但适用于大目录的速度非常慢。我还想将内容写入一个文件(manifest.json) 我更擅长编写C#.NET(我会使用Directory.EnumerateFiles()) 但我想如果我不能在powershell中更轻松地完成简单的事情,我会看看的 但当我进入10万个条目时,这个脚本真的陷入了困境 $src = "G:\wwwroo

我有一个简单的PowerShell脚本,它在目录树中运行,并以JSON格式列出文件

每个条目的格式如下:

{id:filename,大小:bytes}

适用于短列表,但适用于大目录的速度非常慢。我还想将内容写入一个文件(manifest.json)

我更擅长编写C#.NET(我会使用Directory.EnumerateFiles())

但我想如果我不能在powershell中更轻松地完成简单的事情,我会看看的

但当我进入10万个条目时,这个脚本真的陷入了困境

$src = "G:\wwwroot\BaseMaps\BigBlueMarble"
$path = $src + "\*"
$excludes = @("*.json", "*.ps1")
$version = "1.1"
Write-Host "{" 
Write-Host "`"manifest-version`": `"$version`","
Write-Host "`"files`": [" 

$dirs = Get-Item -Path $path -Exclude $excludes 
$dirs | Get-ChildItem -Recurse -File | % { 
    $fpath = $_.FullName.Replace($src, "").Replace("\","/")
    $date = $_.LastWriteTime
    $size = $_.Length
    $id = $_.BaseName
    Write-Host "{`"id`": `"$id`", `"size`": `"$size`"},"
    } 
Write-Host "]"
Write-Host "}"

Get ChildItem
可能很慢(尽管它在PowerShell 3中的速度似乎是v2中的两倍左右),
write host
也让您的速度慢了很多。在包含27000多个文件的目录结构上,以下代码运行时间为16.15秒,而您的代码运行时间为21.08秒。在一个包含大约2400个文件的较小目录中,它是1.15秒对1.22秒

gci $path -file -Recurse |
select @{name="fpath";expression={$_.fullname.replace($src,"").replace("\","/")}},lastwritetime,@{Name="size";Expression={$_.length}},@{Name="id";Expression={$_.basename}}|
select id,size|
ConvertTo-Json
生成的JSON没有您的头,但您应该能够在事后处理它。

在我的系统上:

$pf = "C:\Program Files" # has about 50,000 files
measure-command {$a=[io.Directory]::EnumerateFiles($pf,"*","AllDirectories")|%{$_}}
大约是以下速度的两倍:

measure-command {$a=gci "C:\Program Files" -Recurse}
要点是,您可以非常轻松地使用Powershell中的.NET类,它们可能工作得更好


在这种情况下,get childitem命令有自己的.NET类要执行,并调用文件系统提供程序类,该类无疑调用了[io.directory]中的某些内容。因此,尽管powershell提供程序的概念非常酷,但它确实增加了运行时开销。

有时候,只在C#和.NET中编写实用程序可能更好。使用一个非常方便的库,我组装了一个WPF应用程序,它允许我选择一个文件夹(其中一个有100K个PNG文件),然后在不到2秒钟的时间内创建我在上面尝试过的json“manifest”。下面是应用程序的非UI工作程序部分。感谢上面的提示,它们很有帮助

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows;
using Newtonsoft.Json;

namespace Manifest
{
    internal class Worker
    {
        private DateTime start;
        private ViewModel vm;
        private readonly BackgroundWorker worker = new BackgroundWorker();
        private ManifestObject manifest;

        public Worker()
        {
            vm = ViewModel.myself;
            manifest = new ManifestObject();
            manifest.version = "1.1";
            manifest.files = new List<FileData>();
            worker.DoWork += build;
            worker.RunWorkerCompleted += done;
            worker.RunWorkerAsync();
        }

        public void build(object sender, DoWorkEventArgs e)
        {

            vm.Status = "Working...";
            start = DateTime.Now;
            scan();
        }

        private void scan()
        {
            var top = new DirectoryInfo(vm.FolderPath);
            try
            {
                foreach (var fi in top.EnumerateFiles("*" + vm.FileType, SearchOption.TopDirectoryOnly))
                {
                    FileData fd = new FileData();
                    fd.size = fi.Length;
                    fd.id = fi.Name.Replace(vm.FileType, "");
                    manifest.files.Add(fd);
                    vm.FileCount++;
                }
            }
            catch (UnauthorizedAccessException error)
                    {
                        MessageBox.Show("{0}", error.Message);
                    }
        }

        private void done(object sender,RunWorkerCompletedEventArgs e)
        {
            var done = DateTime.Now;
            var elapsed = done - start;
            vm.ElapsedTime = elapsed.ToString();
            vm.Status = "Done Scanning...";
            write();
        }

        private void write()
        {
            File.WriteAllText(vm.FolderPath + @"\manifest.json", JsonConvert.SerializeObject(manifest, Formatting.Indented));
            vm.Status = "Done";
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用System.IO;
使用System.Windows;
使用Newtonsoft.Json;
名称空间清单
{
内部班主任
{
私有日期时间开始;
私有视图模型虚拟机;
private readonly BackgroundWorker worker=new BackgroundWorker();
私有对象清单;
公职人员()
{
vm=ViewModel.com;
manifest=newmanifestobject();
manifest.version=“1.1”;
manifest.files=新列表();
worker.DoWork+=构建;
worker.RunWorkerCompleted+=完成;
worker.RunWorkerAsync();
}
公共无效生成(对象发送方,DoWorkEventArgs e)
{
vm.Status=“正在工作…”;
start=DateTime.Now;
扫描();
}
专用无效扫描()
{
var top=newdirectoryinfo(vm.FolderPath);
尝试
{
foreach(top.EnumerateFiles(“*”+vm.FileType,SearchOption.TopDirectoryOnly)中的变量fi)
{
FileData fd=新的FileData();
fd.尺寸=fi.长度;
fd.id=fi.Name.Replace(vm.FileType,“”);
manifest.files.Add(fd);
vm.FileCount++;
}
}
捕获(UnauthorizedAccessException错误)
{
MessageBox.Show(“{0}”,error.Message);
}
}
私有void done(对象发送方,RunWorkerCompletedEventArgs e)
{
var done=DateTime.Now;
var运行=完成-启动;
vm.ElapsedTime=appeased.ToString();
vm.Status=“完成扫描…”;
write();
}
私有无效写入()
{
writealText(vm.FolderPath+@“\manifest.json”,JsonConvert.SerializeObject(manifest,Formatting.Indented));
vm.Status=“完成”;
}
}
}

获取子项的速度很慢。为此,最好坚持使用C#/.net。请看一个类似的问题。您是否衡量了每个步骤的性能,以了解瓶颈在哪里?“大目录”中有多少文件?您是否有可用的PowerShell 3(其中包括
转换为JSON
,可能比字符串连接更快)?我认为PowerShell版本/OS版本在这里很重要。您正在运行什么版本?(请参阅我对用户2460798的评论)我正在win7操作系统上运行PowerShell 3.0。Alienware 4芯区域51。5千个文件条目的速度变慢了,我有大约10千个文件夹,其中一个最多有50千个。是的,这对于NTFS来说是非常重要的,但我不控制体系结构的这一部分,事实上,JSON索引是我用来解决这个问题的(通过将其加载到Couchbase DB中)。文件夹中的10K文件大约需要8小时,50K文件在周末运行。因此,我非常倾向于使用DirectoryInfo类并使用:public IEnumerable EnumerateFiles()。讽刺的是,在我的机器上,
measure命令{$a=gci“C:\Program Files”-Recurse}
实际上更快。(枚举文件为1s,枚举文件为3s)这是Windows 8上的powershell 3。这令人惊讶。把我的解释抛诸脑后。我正在WS08上的Win7虚拟机中使用VirtualBox运行PS v3。gci范围为8.6至9.4s(4次运行),其余3.25至4.5s,超过5次运行。