繁体   English   中英

Powershell - 在 foreach 循环中使用 out-file 的性能问题

[英]Powershell - performance issues using out-file in a foreach loop

以下脚本将 XML 转换为特定的 CSV 格式以提供特定的源系统。 它工作正常,但性能非常慢。 我相信问题是因为 Out-File 正在打开 - 关闭每一行的文件。 有一个更好的方法吗?

$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml'
$result = Invoke-RestMethod  -Uri $url
$elements = $result.Envelope.Cube

foreach($element in $elements)
{
    foreach($x in $element.Cube)
    {
        foreach($y in $x.Cube)
        {
            $time = $x.time.ToString() -replace "-"
            $output =  $time + "`t" + $y.currency.ToString() + "`t" + $y.rate.ToString()
            $output | Out-File -Append ".\rates.csv"
        }
    }   
}

我相信问题是因为 Out-File 正在打开 - 关闭每一行的文件

这确实是原因,因此加快命令速度的关键是将 pipe 的所有数据调用到Out-File单个调用中,您可以通过将foreach循环包装在您的脚本块{... } )中来实现使用&调用, 调用运算符

& {
  foreach ($element in $elements) {
    foreach ($x in $element.Cube) {
      foreach ($y in $x.Cube) {
        $time = $x.time.ToString() -replace "-"
        # Synthesize and output the line to save.
        $time + "`t" + $y.currency.ToString() + "`t" + $y.rate.ToString()
      }
    }   
  }
} | Out-File .\rates.csv

以上保留了 PowerShell 的典型流式管道行为,将 output 行一一发送到Out-File

鉴于您的数据已经在 memory 中,您可以通过在foreach循环周围使用$(...)而不是& {... }来加快操作速度,即使用$()子表达式 operator


也就是说,内存数据允许更快的处理

  • 通过绕过管道,而是将所有 output 行作为参数传递。

  • 此外,假设您正在将文本保存到文件中,通过使用Set-Content来加快速度。

    • 注意:在Windows PowerShell中, Set-Content的默认编码不同于Out-File的:活动 ANSI 代码页的编码与 UTF-16LE(“Unicode”); PowerShell [Core] 7+中,所有 cmdlet 始终默认为无 BOM 的 UTF-8; 根据需要使用-Encoding参数。
  • 最后,您可以利用 PowerShell 的成员枚举foreach循环中消除一层嵌套。

# Adjust -Encoding as needed.
Set-Content -Encoding utf8 .\rates.csv -Value $(
  foreach ($x in $elements.Cube) {
    foreach ($y in $x.Cube) {
      $time = $x.time.ToString() -replace "-"
      # Synthesize and output the output line.
      $time + "`t" + $y.currency.ToString() + "`t" + $y.rate.ToString()
    }
  }   
)

尝试这个:

$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml'
$result = Invoke-RestMethod  -Uri $url
$elements = $result.Envelope.Cube

$outputAll = New-Object -TypeName "System.Collections.ArrayList"

foreach($element in $elements)
{
    foreach($x in $element.Cube)
    {
        foreach($y in $x.Cube)
        {
            $time = $x.time.ToString() -replace "-"
            $output =  $time + "`t" + $y.currency.ToString() + "`t" + $y.rate.ToString()
            $null = $outputAll.add($output)
        }
    }   
}

$outputAll | Out-File -Append ".\rates.csv"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM