Performance PowerShell中的嵌套间隔(c)/比具有排序列表的“where object”更快的替代方案?

Performance PowerShell中的嵌套间隔(c)/比具有排序列表的“where object”更快的替代方案?,performance,powershell,sorting,filtering,where-clause,Performance,Powershell,Sorting,Filtering,Where Clause,我正在寻找一种方法来加速Windows 10 PowerShell命令,其中包含排序数组的对象 最后,该数组将包含来自日志文件的数千行。日志文件中的所有行都以日期和时间开始,并按日期/时间排序。始终会追加新行 以下命令可以工作,但对于排序数组来说速度非常慢且无效: $arrFileContent | where {($_ -ge $Start) -and ($_ -le $End)} 下面是一个非常简化的示例: $arrFileContent = @("Bernie", "Emily", "F

我正在寻找一种方法来加速Windows 10 PowerShell命令,其中包含排序数组的对象

最后,该数组将包含来自日志文件的数千行。日志文件中的所有行都以日期和时间开始,并按日期/时间排序。始终会追加新行

以下命令可以工作,但对于排序数组来说速度非常慢且无效:

$arrFileContent | where {($_ -ge $Start) -and ($_ -le $End)}
下面是一个非常简化的示例:

$arrFileContent = @("Bernie", "Emily", "Fred", "Jake", "Keith", "Maria", "Paul", "Richard", "Sally", "Tim", "Victor")
$Start = "E"
$End = "P"
预期结果:艾米丽、弗雷德、杰克、基思、玛丽亚、保罗

我想,使用嵌套区间应该快得多,比如找到第一个以E或以上开头的条目,以及第一个以p或以下开头的条目,然后返回中间的所有条目。 我想一定有一个简单的PowerShell或.NET解决方案,所以我不必自己编写代码,对吗? 编辑31.08.19:不确定嵌套区间德语Intervallschachtelung是否正确。 我的意思是电话簿的原则:在中间打开书,检查是否要在前面或之后列出想要的名字,在第一或最后一半的时候打开书,等等。 在这种情况下,检查给定日期范围内日志文件的100.000行: -检查线路编号50.000 -如果在给定的开始日期之后,检查行号75.000,否则检查行号25.000 -检查线路号75.000或25.000 -如果在给定的开始日期之后,检查第87.500行或。。。其他支票号码62.500或。。。 等等 日志文件包含如下行: 2018-01-17 14:28:19安装xxx开始
只有更多的文字

我们才能衡量评论中提到的所有方式。让我们使用Get ChildItem模拟日志文件中的数千行:

使用Get-ChildItem d:\bat\*:

使用Get-ChildItem d:\*输出更多名称:


简单地替换如下内容也是有效的

$arr| where{} ↓ $arr |&{process{if{${}}


对我来说,嵌套间隔是指间隔内的间隔。我想我应该描述一下你想要做的是选择一个范围。我们可以利用这样一个事实,即对数据进行排序,以便在找到范围的结尾时立即停止枚举

NET的LINQ查询允许我们轻松地完成这项工作。假设Names.txt包含此内容

…在C语言中,过滤将像

IEnumerable<string> filteredNames = System.IO.File.ReadLines("Names.txt")
    .Where(name => name[0] >= 'E')
    .TakeWhile(name => name[0] <= 'P');
为了实现这一点,您必须直接调用静态LINQ方法并获得所有正确的类型。因此,Where的第一个参数是System.Collections.Generic.IEnumerable[String],这就是ReadLines返回的内容,这就是为什么我使用一个文件来进行此操作。Where和TakeWhile的谓词参数的类型为[Func[String,Boolean]]一个接受字符串并返回布尔值的函数,这就是脚本块必须显式转换为该类型的原因

此代码执行后,$filteredNames将包含一个查询对象;也就是说,它不包含结果,而是如何获得结果的蓝图

如果要多次访问结果,应将其存储在数组中,以避免多次读取文件

PS> $filteredNames = [System.Linq.Enumerable]::ToArray($filteredNames)
PS> $filteredNames.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String[]                                 System.Array


PS> $filteredNames
Emily
Fred
Jake
Keith
Maria
Paul

我尝试了@josefz答案的变体。当我越过我想要的最后一条线时,我没有得到惊人的结果。事实上,如果只是“a”到“b”,我会节省一分钟。除非缓慢是因为获取内容?获取内容日志将比获取内容-读取计数-1日志慢

$arrFileContent=Get ChildItem-name-File-Recurse | select-first 89570 | sort-u $start='e' $end='p' 测量命令{ $arrfileslowned=foreach$OneName在$arrFileContent中{ 如果$OneName-ge$Start{ 如果$OneName-le$End{ $OneName } } } }| fl秒,毫秒 早睡 测量命令{ $arrfileslowned=foreach$OneName在$arrFileContent中{ 如果$OneName-ge$Start{ 如果$OneName-le$End{ $OneName }否则{ 打破 } } } }| fl秒,毫秒 输出:

秒:1 毫秒:207 秒:1 毫秒:174 正在尝试获取内容与切换-文件:

$start='e' $end='p' 使用更多内存 测量命令{ $result1=get content-readcount-1 log | foreach{$| 其中{$\ge$start-和$\le$end} }| fl秒,毫秒 测量命令{ $result2=开关-文件日志{ {${ge$start-和${le$end}{$} }| fl秒,毫秒 输出:

秒:4 毫秒:491 秒:2 毫秒:747 您可以堆叠具有不同模式的方法,一旦满足条件和模式组合,这些方法将停止处理。例如$arrFileContent.where{$\ge$start},'SkipUntil'。where{$\gt$end},'Until'}。这比我在每个测试用例中所做的都要快。但它足够快还是最理想?我不知道。Group Object cmdlet可以使用计算道具进行分组。//Where-Object cmdlet是一段相当慢的代码—它是h
由于跟踪的内容太多,所以您可能需要使用一种更快的技术。[1] 在$Collection中,foreach$Thing通常快一个数量级。[2] $Collection.Where方法通常至少快50%。@Lee_Dailey数组收集方法的唯一问题是整个数组都加载到内存中,以便首先处理。@TheIncorrigible1-是的,这是使用管道的原因之一。[grin]然而,在管道中调试东西确实令人不快,因此一个otta从简单的结构开始,比如基本的foreach循环,并且只在需要时进行优化。关于使用日志文件详细信息进行编辑,这些行号来自哪里?第50000行是否参考了第75000行和第25000行?或者你真的想用邮戳做一件事?这似乎完全改变了问题的性质,我看不出这与第一个字母在范围E-P中的选定名称有什么关系,这实际上是一个由线性搜索确定的动态范围/间隔。谢谢帮助。很抱歉搞混了。很明显,我的翻译错了,或者对问题描述得不够清楚。您确定这也适用于以日期/时间开头的100000行,并将每个日期/时间与给定范围进行比较吗?Linq是否也必须至少在最坏的情况下/根据给定的范围比较所有100.000行-这需要相当长的时间?或者您可以让Linq知道数组已排序吗?需要对其进行调整,以处理问题中未显示的日志文件,但是,是的,这非常适合从大量已排序数据中选择一系列记录,特别是文件数据,因为这样您就可以将其作为读取内容进行过滤,而不是将整个内容加载到内存中。否,通过使用TakeWhile,当它找到$name[0]-le[Char]“P”返回$false的元素时,它将停止枚举。LINQ不需要被告知数据已排序;这取决于程序员为他们的数据选择合适的方法,这就是我在这里所做的。我只是编辑了问题描述并添加了一些详细信息。读取文件应该不是问题。虽然获取内容的速度确实非常慢,而且有更快的方法获取内容-Raw、System.IO.StreamReader。。。在这种情况下没关系。即使有100.000行和大约22MB,也只需要1到2秒,而检查日期范围或其他内容则需要1分钟。当文件内容已经被读取到数组中时,我的问题就出现了。顺便说一下:我创建了一个测试文件,它与我上面提到的日志文件非常相似:$strLogPath=c:\temp\test.log$strText=+x*200$datStart=Get Date 2019-07-01 00:00&{foreach$intCount in@1..100000{$datStart.ToString'yyyy-MM-dd HH:MM:ss'+$strText$datStart=$datStart.AddMinutes1}|设置Content-Path$strLogPath-Encoding UTF8-Hmm,即使使用您的示例,|其中$start='2019-07-14 21:19'只需要1.9秒;$end='2019-08-11 15:59',20%和60%进入文件。如果处于底部,则使用中断的foreach获胜。@josefzEven better是使用中断的foreach。在if之前有一个额外的括号。
2777
Where-Object  111,5433    535
Where method  56,8577 535
foreach + if  6,542   535
D:\PShell\SO\56993333.ps1
89570
Where-Object  4056,604    34087
Where method  1636,9539   34087
foreach + if  422,8259    34087
$arrFileContent | & { process { if ($_ -ge $Start -and $_ -lt $End) { $_ } } }
Bernie
Emily
Fred
Jake
Keith
Maria
Paul
Richard
Sally
Tim
Victor
IEnumerable<string> filteredNames = System.IO.File.ReadLines("Names.txt")
    .Where(name => name[0] >= 'E')
    .TakeWhile(name => name[0] <= 'P');
$source = [System.IO.File]::ReadLines($inputFilePath)
$rangeStartPredicate = [Func[String, Boolean]] {
    $name = $args[0]

    return $name[0] -ge [Char] 'E'
}
$rangeEndPredicate = [Func[String, Boolean]] {
    $name = $args[0]

    return $name[0] -le [Char] 'P'
}

$filteredNames = [System.Linq.Enumerable]::TakeWhile(
    [System.Linq.Enumerable]::Where($source, $rangeStartPredicate),
    $rangeEndPredicate
)
PS> $filteredNames.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
False    False    <TakeWhileIterator>d__27`1               System.Object
PS> $filteredNames
Emily
Fred
Jake
Keith
Maria
Paul
PS> $filteredNames = [System.Linq.Enumerable]::ToArray($filteredNames)
PS> $filteredNames.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String[]                                 System.Array


PS> $filteredNames
Emily
Fred
Jake
Keith
Maria
Paul