在PowerShell中逐行读取文件

Kin*_*ere 80 powershell powershell-ise

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

我知道Bash的等价物:

while read line do
    if [[ $line =~ $regex ]]; then
          # work here
    fi
done < file.txt
Run Code Online (Sandbox Code Playgroud)

关于PowerShell循环的文档不多.

Mat*_*sen 140

关于PowerShell循环的文档不多.

在PowerShell中环文档充足,你可能想看看下面的帮助主题:about_For,about_ForEach,about_Do,about_While.

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

另一个惯用的PowerShell解决方案是将文本文件的行传递给ForEach-Objectcmdlet:

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

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

Get-Content .\file.txt | Where-Object {$_ -match $regex} | ForEach-Object {
    # Work here
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,但是,“简洁”和“清晰”是两个不同的东西。如果您需要任何人阅读这个脚本,那么我恳求您 - 不要对我们这样做。 (8认同)
  • 当您尝试读取大文件时,"Get-Content"非常糟糕 (4认同)
  • 最后一个是 powershell 最惯用的,可以用 `gc 'file.txt' | 写得更简洁。?{ $_ -匹配 $regex } | %{ &lt;#stuff#&gt; }` (3认同)

Kol*_*yon 44

Get-Content表现不好; 它试图一次将文件读入内存.

C#(.NET)文件读取器逐个读取每一行

最佳表现

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

要么

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

foreach声明可能会略快ForEach-Object(请参阅下面的评论以获取更多信息).

  • 这是一个非常普遍的误解.`foreach`是一个声明,如`if`,`for`或`while`.`ForEach-Object`是一个命令,比如`Get-ChildItem`.对于`ForEach-Object`,还有一个默认别名`foreach`,但它仅在有管道时使用.请参阅"Get-Help about_Foreach"中的详细说明,或单击我之前评论中的链接,该链接由Microsoft的The Scripting Guys提供有关语句和命令之间差异的整篇文章. (11认同)
  • 我可能会使用`[System.IO.File] :: ReadLines("C:\ path\to\file.txt")| ForEach-Object {...}`.`foreach`语句将[将整个集合加载到一个对象](https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/getting-to-know-foreach-and-foreach-object/ ).`ForEach-Object`使用管道进行流式传输.现在`foreach`语句可能比`ForEach-Object`命令略快,但这是因为将整个事物加载到内存通常更快.然而,"Get-Content"仍然很糟糕. (2认同)
  • @BaconBits https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/getting-to-know-foreach-and-foreach-object/学到了新的东西.谢谢.我认为它们是相同的,因为`Get-Alias foreach` =>`Foreach-Object`,但你是对的,有差异 (2认同)
  • 这将有效,但你想在循环的脚本块中将`$ line`更改为`$ _`. (2认同)

Tar*_*aif 14

逐行读取大文件

原始评论 (1/2021) 我能够通过以下内容在大约 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() 
Run Code Online (Sandbox Code Playgroud)

附录 (3/2022) 使用 PowerShell 中嵌入的 C# 处理大文件速度更快,并且“问题”更少。

$code = @"
using System;
using System.IO;

namespace ProcessLargeFile
{
    public class Program
    {
        static void ProcessLine(string line)
        {
            return;
        }

        public static void ProcessLogFile(string path) {
            var start_time = DateTime.Now;
            StreamReader sr = new StreamReader(File.Open(path, FileMode.Open));
            try {
                while (!sr.EndOfStream){
                    string line = sr.ReadLine();
                    ProcessLine(line);
                }
            } finally {
                sr.Close();
            }
            var end_time = DateTime.Now;
            var run_time = end_time - start_time;
            string msg = "Completed in " + run_time.Minutes + ":" + run_time.Seconds + "." + run_time.Milliseconds;
            Console.WriteLine(msg);
        }

        static void Main(string[] args)
        {
            ProcessLogFile("c:\\users\\tasaif\\fake.log");
            Console.ReadLine();
        }
    }
}
"@
 
Add-Type -TypeDefinition $code -Language CSharp

PS C:\Users\tasaif> [ProcessLargeFile.Program]::ProcessLogFile("c:\\users\\tasaif\\fake.log")
Completed in 0:17.109
Run Code Online (Sandbox Code Playgroud)


js2*_*010 6

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

'one
two
three' > file

$regex = '^t'

switch -regex -file file { 
  $regex { "line is $_" } 
}
Run Code Online (Sandbox Code Playgroud)

输出:

line is two
line is three
Run Code Online (Sandbox Code Playgroud)


Gue*_*477 5


Set-Location 'C:\files'
$files = Get-ChildItem -Name -Include *.txt
foreach($file in $files){
        Write-Host("Start Reading file: " + $file)
        foreach($line in Get-Content $file){
            Write-Host($line)
        }
        Write-Host("End Reading file: " + $file)                
}

Run Code Online (Sandbox Code Playgroud)