PowerShellの落とし穴!ForEach-Objectとforeachは違う?

こんにちは。チェシャ男です。(-皿-)

今回は、PowerShell におけるforeach のコマンドレットとステートメントの違いについて少しお話しします。

PowerShell では「ForEach-Object コマンドレット」と、そのエイリアス「foreach」、さらには「foreach ステートメント」と同じような構文がいくつもあり使い方の違いに戸惑ってしまいます。

今回は Foreach ループにおける「コマンドレット」と「ステートメント」の動きのについて解説します。

Foreach-Object コマンドレットに関する基本的な操作方法についてはこちら↓の記事をご参考ください。

[blogcard url=”https://cheshire-wara.com/powershell/ps-cmdlets/object/foreach-object”]

foreachステートメントについて

あなたがPowerShell 中級者他のプログラミング言語を触ったことがあるならばこういったループ処理の記述をするかもしれません。

PS C:\work> #違う書き方のバージョン
PS C:\work> $Files = Get-ChildItem -Name
PS C:\work> foreach ($file in $files) {
>> "C:\work\" + $file
>> }
C:\work\File1.txt
C:\work\File2.txt
C:\work\File3.txt
C:\work\File4.txt
C:\work\File5.txt
PS C:\work>
PS C:\work> #または最初の処理をまとめて
PS C:\work>  foreach ($file in (Get-ChildItem -Name)) {
>> "C:\work\" + $file
>> }
C:\work\File1.txt
C:\work\File2.txt
C:\work\File3.txt
C:\work\File4.txt
C:\work\File5.txt

このような「foreach ( 変数 in オブジェクト)」を使ったループの書き方は「ForEach-Object」と何が違うんだろう。そう思うかもしれません。

[blogcard url=”https://cheshire-wara.com/powershell/ps-cmdlets/object/foreach-object-cmdlets/”]

上の記事にあるように、ForEach-Object  エイリアスに「foreach」があるので非常にわかり辛いんです。

ForEach-Object  マンドレットに対し、「foreach ( 変数 in オブジェクト)」のように使う場合は「foreach ステートメント」といいます。

【ステートメントとは】
ステートメントとは、発言、声明、宣言、供述、などの意味を持つ英単語。プログラミングで、制御や宣言などを行うために言語仕様にあらかじめ組み込まれている命令語、および、それらを用いて記述された一つの命令文のこと。変数・定数・関数などの宣言や、条件分岐や繰り返しの制御などを行うためのものが多い。

ステートメントとは|statement - 意味 – 定義 : IT用語辞典/© 1997- 2017 Incept Inc. 

この説明だけだと、いまいち違いがわかりませんね。

コマンドレットとステートメントの違い

コマンドレットとステートメントでは構文(書き方)が大きく違うこともありますが実は内部的な処理方法にも違いがあります。

  • ForEach-Object(foreach) マンドレット
    →オブジェクト内をループしながら1つずつ処理する
  • foreach テートメント
    →オブジェクト内をメモリにまとめてため込み一気に処理

つまり、つけ麺で例えると

  • ForEach-Object (foreach) コマンドレット
    →麺を食べる分だけ箸でつかみスープに浸けて食べる
  • foreachステートメント
    →麺をすべてスープに入れて一気に食べる

という風な違いがあります。(笑)

食べる分だけ浸けて食べる場合なら小さな器で問題ありませんが、麺をすべて入れるとなるとそれなりに大きな器が必要ですよね?

同様にforeachステートメントの場合は、ForEach-Object コマンドレットで処理する場合よりもメモリの使用量が多くなります。

これだけ書くとデメリットしかないように見えますが、

ステートメント:ループ処理に必要なデータを1度にメモリへ格納して処理する場合

コマンドレット:ループ処理に必要なデータを1データずつメモリに格納して処理する場合

とでは「オブジェクトの生成と破棄」という動作の回数が増えるためが実行速度に差が出てきます。

ですので、「どちらの方法を選択するか」は”処理パフォーマンス”と”リソース(メモリ)”のバランスから場面によって使い分けることが望ましいと考えられます。

使い方の復習

foreach エイリアス(コマンドレット)として実行する場合は

<コマンドレット> | foreach -Process {<処理内容>}

or

<コマンドレット> | ForEach-Object -Process {<処理内容>}

を使用する。

foreach ステートメントを使用する場合は、

foreach ( 変数 in オブジェクト) {<処理内容>}

を使用する。

どちらも同じループ処理を記述するものですが、どちらを使用するかについては”処理パフォーマンス”と”リソース(メモリ)”のバランスで使い分けましょう!

 

2 COMMENTS

チェシャ 男 へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。