3

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

每个条目的形式为:

{id:文件名,大小:字节}

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

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

但我想我会看看我是否不能在 powershell 中更轻松地完成简单的事情。

但是当我达到 10K 条目时,这个脚本真的陷入了困境。

$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 "}"
4

3 回答 3

2

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

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 没有您的标头,但您应该能够在事后处理它。

于 2013-06-26T16:39:41.983 回答
1

有时最好只用 C# 和 .NET 编写实用程序。使用一个非常方便的JSON.NET库,我组合了一个 WPF 应用程序,它让我可以选择一个文件夹(其中一个有 100K PNG 文件),然后在不到 2 秒的时间内创建我上面尝试过的 json“清单”。这是应用程序的非 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";
        }
    }
}
于 2013-07-01T19:54:56.707 回答
1

在我的系统上:

$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 提供程序的概念非常酷,但它确实增加了运行时开销。

于 2013-06-27T02:41:49.743 回答