在PowerShell中逐行读取文件


100

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

我知道Bash等效:

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

关于PowerShell循环的文档不多。


Mathias选择的答案不是一个很好的解决方案。Get-Content一次将整个文件加载到内存中,这将失败或冻结大文件。
Kolob Canyon

@KolobCanyon完全不正确。默认情况下,Get-Content将每一行作为管道中的一个对象加载。如果要传递给未指定process块的函数,并且每行将另一个对象吐出到管线中,则该函数就是问题所在。将全部内容加载到内存中的任何问题都不是的错Get-Content

@TheFish foreach($line in Get-Content .\file.txt)在开始迭代之前,它将整个文件加载到内存中。如果您不相信我,请获取1GB的日志文件并尝试一下。
Kolob Canyon

1
@KolobCanyon那不是你说的。您说Get-Content将所有内容加载到内存中,这是不正确的。是的,您更改的foreach示例将是;foreach不支持管道。 Get-Content .\file.txt | ForEach-Object -Process {}支持管道,并且不会将整个文件加载到内存中。默认情况下,Get-Content将一次通过管道传递一行。

Answers:


176

关于PowerShell循环的文档不多。

在PowerShell中环文档充足,你可能想看看下面的帮助主题:about_Forabout_ForEachabout_Doabout_While

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

针对您的问题的另一种常用的PowerShell解决方案是将文本文件的行通过管道传递到ForEach-Objectcmdlet

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
}

链接没有断开,但现在重定向到docs.microsoft.com
Peter Mortensen

@KolobCanyon从未在OP上被提及为问题。

51

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(有关更多信息,请参见下面的评论)。


5
我可能会用[System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object { ... }。该foreach语句会将整个集合加载到一个对象ForEach-Object使用管道进行流式传输。现在,该foreach语句可能会比ForEach-Object命令快一些,但这是因为将整个内容加载到内存通常更快。 Get-Content但是仍然很糟糕。
培根

@BaconBits foreach()Foreach-Object
Kolob Canyon

15
这是一个非常普遍的误解。 foreach是一个语句,如ifforwhileForEach-Object是一个命令,例如Get-ChildItem。还有一个foreachfor 的默认别名ForEach-Object,但是仅在有管道时使用。请参阅中的详细说明Get-Help about_Foreach,或单击我以前的评论中的链接,该链接指向Microsoft的The Scripting Guys的整篇文章,内容涉及语句与命令之间的差异。
培根在位

3
@BaconBits blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/... 学到新的东西。谢谢。我以为它们是相同的,因为Get-Alias foreach=> Foreach-Object,但是您是对的,有区别
Kolob Canyon

2
可以,但是您需要在循环的脚本块中将其更改$line$_
培根片

1

万能的开关在这里工作良好:

'one
two
three' > file

$regex = '^t'

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

输出:

line is two
line is three
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.