【问题标题】:How does powershell lazily evaluate this statement?powershell 如何懒惰地评估这个语句?
【发布时间】:2021-07-10 17:48:51
【问题描述】:

我正在寻找一种仅读取 csv 文件前几行的方法,结果遇到了this answer。接受的答案建议使用

Get-Content "C:\start.csv" | select -First 10 | Out-File "C:\stop.csv"

另一个答案建议使用

Get-Content C:\Temp\Test.csv -TotalCount 3

因为我的 csv 相当大,所以我选择了第二个选项。它工作得很好。出于好奇,我决定尝试第一个选项,假设我可以 ctrl+c 如果它需要永远。我惊讶地发现它回来得这么快。

在处理大文件时使用第一种方法是否安全? powershell 是如何做到这一点的?

【问题讨论】:

  • 第二个选项更快,因为没有管道

标签: powershell


【解决方案1】:

是的,Select-Object -First n 对于大文件是“安全的”(前提是您只想读取少量行,因此管道开销将是微不足道的,否则Get-Content -TotalCount n 会更有效)。

它像break 一样在循环中工作,通过提前退出管道,当给定数量的项目已被处理时。在内部,它会引发 PowerShell 管道机制识别的特殊异常。


这是一个“滥用”Select-Object 以打破ForEach-Object“循环”的演示,这是使用普通break 语句无法实现的。

1..10 | ForEach-Object {
   Write-Host $_             # goes directly to console, so is ignored by Select-Object
   if( $_ -ge 3 ) { $true }  # "break" by outputting one item
} | Select-Object -First 1 | Out-Null

输出:

1
2
3

如您所见,Select-Object -First n 实际上打破了管道,而不是先读取所有输入,然后仅选择指定数量的项目。


另一个更常见的用例是当您只想在管道的输出中查找单个项目时。找到该项目后立即退出管道是有意义的:

Get-ChildItem -Recurse | Where-Object { SomeCondition } | Select-Object -First 1

【讨论】:

    【解决方案2】:

    根据Microsoft,Get-Content cmdlet 有一个名为-ReadCount 的参数。他们的文档说明

    指定一次通过管道发送多少行内容。默认值为 1。值 0(零)一次发送所有内容。

    此参数不会改变显示的内容,但会影响显示内容所需的时间。随着 ReadCount 值的增加,返回第一行所需的时间会增加,但操作的总时间会减少。这可以对大型项目产生明显的影响。

    由于-ReadCount 默认为 1,Get-Content 有效地充当了逐行读取文件的生成器。

    【讨论】:

    • 我同意这一点。此外,当您不沿着管道发送对象时,它通常会更快,即使它在时间返回上看起来几乎相同,但通常不是。 令牌分析器词法分析器)必须在将每个 cmdlt 发送到 powershell 执行引擎之前对其进行处理以进行正确解析。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 2021-08-12
    • 2017-01-13
    • 2019-01-07
    • 1970-01-01
    • 2016-05-12
    相关资源
    最近更新 更多