【问题标题】:Read file line by line in PowerShell在 PowerShell 中逐行读取文件
【发布时间】:2016-02-04 08:27:25
【问题描述】:

我想在 PowerShell 中逐行读取文件。具体来说,我想循环遍历文件,将每一行存储在循环中的一个变量中,对行做一些处理。

我知道 Bash 等价物:

while read line do
    if [[ $line =~ $regex ]]; then
          # work here
    fi
done < file.txt

关于 PowerShell 循环的文档不多。

【问题讨论】:

  • 从 Mathias 选择的答案不是一个很好的解决方案。 Get-Content 一次将整个文件加载到内存中,这将失败或冻结大文件。
  • @KolobCanyon 这完全不真实。默认情况下,Get-Content 将每一行加载为管道中的一个对象。如果您要传递到一个未指定 process 块的函数,并且每行将另一个对象吐出到管道中,那么该函数就是问题所在。将全部内容加载到内存中的任何问题都不是Get-Content 的错。
  • @TheFish foreach($line in Get-Content .\file.txt) 它将在开始迭代之前将整个文件加载到内存中。如果您不相信我,请获取一个 1GB 的日志文件并尝试一下。
  • @KolobCanyon 这不是你说的。您说 Get-Content 将其全部加载到内存中,这是不正确的。您更改的 foreach 示例会,是的; foreach 不支持管道。 Get-Content .\file.txt | ForEach-Object -Process {} 是管道感知的,不会将整个文件加载到内存中。默认情况下,Get-Content 将通过管道一次传递一行。

标签: powershell powershell-ise


【解决方案1】:

关于 PowerShell 循环的文档不多。

有关 PowerShell 中循环的文档非常丰富,您可能需要查看以下帮助主题:about_Forabout_ForEachabout_Doabout_While

foreach($line in Get-Content .\file.txt) {
    if($line -match $regex){
        # Work here
    }
}

另一个惯用的 PowerShell 解决方案是将文本文件的行通过管道传输到 ForEach-Object cmdlet:

Get-Content .\file.txt | ForEach-Object {
    if($_ -match $regex){
        # Work here
    }
}

您可以通过Where-Object 将这些行通过管道传递给循环,而不是在循环内进行正则表达式匹配,以仅过滤您感兴趣的那些:

Get-Content .\file.txt | Where-Object {$_ -match $regex} | ForEach-Object {
    # Work here
}

【讨论】:

  • 最后一个是powershell最惯用的,用gc 'file.txt' | ?{ $_ -match $regex } | %{ &lt;#stuff#&gt; }写更简洁
【解决方案2】:

Get-Content 性能不佳;它会尝试一次将文件全部读入内存。

C# (.NET) 文件阅读器逐行读取

最佳表现

foreach($line in [System.IO.File]::ReadLines("C:\path\to\file.txt"))
{
       $line
}

或性能稍差

[System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object {
       $_
}

foreach 语句可能会比ForEach-Object 稍快一些(有关更多信息,请参阅下面的 cmets)。

【讨论】:

  • 我可能会使用[System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object { ... }foreach 语句将 load the entire collection to an objectForEach-Object 使用管道进行流式传输。现在foreach 语句可能会比ForEach-Object 命令稍微快一点,但那是因为将整个内容加载到内存中通常更快。不过,Get-Content 仍然很糟糕。
  • 这是一个很常见的误解。 foreach 是一个语句,如 ifforwhileForEach-Object 是一个命令,例如 Get-ChildItemForEach-Object还有一个默认别名foreach,但只有在有管道的情况下才使用。请参阅Get-Help about_Foreach 中的详细说明,或单击我之前评论中的链接,该链接指向 Microsoft 脚本专家关于语句和命令之间差异的整篇文章。
  • @BaconBits blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/… 学到了一些新东西。谢谢。我认为它们是相同的,因为 Get-Alias foreach => Foreach-Object,但你是对的,存在差异
  • 这行得通,但您需要在循环的脚本块中将 $line 更改为 $_
  • @TheFish 是的,但这是一个典型的问题,我认为人们应该知道使用 Get-Content 是魔鬼。
【解决方案3】:

万能的switch在这里工作得很好:

'one
two
three' > file

$regex = '^t'

switch -regex -file file { 
  $regex { "line is $_" } 
}

输出:

line is two
line is three

【讨论】:

    【解决方案4】:

    使用以下命令,我能够在大约 50 秒内读取 4GB 的日志文件。您可以通过使用 PowerShell 将其作为 C# 程序集动态加载来加快速度。

    [System.IO.StreamReader]$sr = [System.IO.File]::Open($file, [System.IO.FileMode]::Open)
    while (-not $sr.EndOfStream){
        $line = $sr.ReadLine()
    }
    $sr.Close() 
    

    【讨论】:

      猜你喜欢
      • 2019-05-14
      • 2022-01-25
      • 1970-01-01
      • 2011-06-15
      • 2010-12-24
      • 2019-04-16
      • 2012-02-04
      • 2010-11-19
      相关资源
      最近更新 更多