PowerShellで1行ずつファイルを読み取る


100

PowerShellでファイルを1行ずつ読みたいのですが。具体的には、ファイルをループして、各行をループ内の変数に格納し、その行でいくつかの処理を行います。

私はBashの同等物を知っています:

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

PowerShellループに関するドキュメントは多くありません。


Mathiasから選択された回答は、優れたソリューションではありません。Get-Contentファイル全体を一度にメモリにロードします。これは、大きなファイルで失敗またはフリーズします。
コロブキャニオン

@KolobCanyonそれは完全に偽りです。既定では、Get-Contentは各行をパイプラインの1つのオブジェクトとして読み込みます。processブロックを指定しない関数にパイプし、パイプラインに1行ごとに別のオブジェクトを吐き出す場合は、その関数が問題です。完全なコンテンツをメモリにロードする際の問題は、のせいではありません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は一度に1行をパイプライン経由で渡します。

回答:


176

PowerShellループに関するドキュメントは多くありません。

PowerShellでのループ上のドキュメントが豊富で、次のヘルプトピックをチェックアウトする場合があります:about_Forabout_ForEachabout_Doabout_While

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

問題に対する別の慣用的なPowerShellソリューションは、テキストファイルの行をForEach-Objectコマンドレットにパイプ処理することです。

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 2017

OPの問題として言及されたことのない@KolobCanyon。
フィッシュ

51

Get-Contentパフォーマンスが悪い; ファイルを一度にメモリに読み込もうとします。

C#(.NET)ファイルリーダーが各行を1つずつ読み取ります

最高のパフォーマンス

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
Canyon

15
これはよくある誤解です。 foreach文のようであるifforまたはwhileForEach-ObjectのようなコマンドGet-ChildItemです。foreachforのデフォルトのエイリアスもありますForEach-Objectが、これはパイプラインがある場合にのみ使用されます。の詳細な説明を参照するGet-Help about_Foreachか、以前のコメントのリンクをクリックして、ステートメントとコマンドの違いに関するMicrosoftのThe Scripting Guysによる記事全体に移動してください。
ベーコンビット

3
@ BaconBitsblogs.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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.