PowerShellで無限ループを作る方法や、処理完了までリトライする(待つ)方法
本エントリーの目次
PowerShellで無限ループを使った処理を作りたい!
PowerShellを使った自動化処理やスクリプトファイルを作成している際、無限ループを使った処理を作りたい!
と感じることがあります。
たとえば別のプログラムの処理や、非同期実行している処理の完了を待つようなケースでの使用が考えられます。
そこで今回は、PowerShellで無限ループを作る方法や、無限ループを使って何らかの処理の完了を待つ(処理が完了するまでリトライする)方法。
また、リトライ回数に上限を設定して処理を行う方法をご紹介します!
PowerShellで無限ループを作成する方法
PowerShellで無限ループを作成する方法は複数考えられますが、最も使いやすいのは『while』を使った無限ループでしょう。
PowerShellの『while』は、後ろの()内に記述された評価式の結果がTrue(PowerShellでは$true)である間は繰り返す。
という動作を行います。
そのため、以下のように『while ($true){<無限ループの中で実行したい処理>}』という構文により、無限ループを作成可能です。
1 2 3 4 5 6 7 | while ($true){ #ここに無限ループの中で実行したい処理を記述する。 #処理を強制的に終了したい場合には、キーボードの『Ctrl』キーを押しながら、『C』キーを押下する。 } |
『while』の後ろの()内に『$true』が記述されており、評価結果は常にTrueとなります。
これにより、繰り返し動作がいつまでも続く無限ループの動作を行う、というわけです。
以下は、無限ループを使って数字をカウントアップする処理のサンプルスクリプトです。
1 2 3 4 5 6 | $Count = 0 while ($true){ $Count++ Write-Host $Count } |
このスクリプトを実行すると、以下のような結果が出力されます。
1 2 3 4 | 1 2 3 以下4、5、6 …と増分しながら、数字が繰り返し出力される。 |
尚、上記スクリプトを実行すると、高速で無限ループによる繰り返し動作が実行されます。
そして手動で強制終了を行うまでは、繰り返し動作が終了しません。(無限ループなので当たり前といえば当たり前ですが…。)
手動でPowerShellスクリプトの処理を強制終了する場合は、キーボードの『Ctrl』キーを押しながら『C』キーを押下してください。
この『Ctrl』+『C』ショートカットキーの実行により、実行中のPowerShellの処理がただちに終了します。
無限ループの繰り返し処理を、一定間隔で実行する(次の処理まで一定時間待ってから実行する)方法
先に紹介したサンプルスクリプトの例では、無限ループによる繰り返し処理が高速で実行されてしまいます。
ですが実際に無限ループを使った処理を実装する際には、一度処理が終わったら次の繰り返し処理は1秒後に開始する。
といったように、繰り返し処理を一定間隔で実行(次の処理まで一定時間待ってから実行)したい!
なんてケースが多いんじゃないでしょうか。
この場合には、繰り返し処理のどこかに『Start-Sleep』コマンドレットを使った一時停止処理を実装すると良いでしょう。
先ほどのサンプルスクリプトを、『Start-Sleep』コマンドレットを使って1秒おきに繰り返すような動作に変更する場合、以下のような記述を行います。
1 2 3 4 5 6 7 | $Count = 0 while ($true){ $Count++ Write-Host $Count Start-Sleep -Seconds 1 } |
尚、『Start-Sleep』コマンドレットでは待ち時間を秒単位ではなく、ミリ秒単位で指定することも可能です。
ミリ秒単位での指定により1秒間処理を一時停止したい場合には、以下のように記述してください。
1 | Start-Sleep -Milliseconds 1000 |
PowerShellで無限ループを使って、何らかの処理の完了を待つ(処理が完了するまでリトライする)方法
無限ループを使うことで、何らかの処理の完了を待つ(処理が完了するまでリトライする)ことも可能です。
冒頭にも書いた別のプログラムの処理や、非同期実行している処理の完了を待つようなケースでの使用が考えられます。
PowerShellの『while』は、後ろの()内に記述された評価式の結果がTrue(PowerShellでは$true)である間は繰り返す。
という仕様であるため、何らかの処理が完了したら『while』の評価式の結果がFalseとなるような記述をすればOKです。
たとえば『C:\sample\test.txt』というパスに、ファイルが作成されるまで待機する。
また、ファイルが作られているかどうかのチェックは1秒おきに実行する。
というような動作を実現したい場合には、以下のような処理を記述します。
1 2 3 4 5 | while (!(Test-Path -Path "C:\sample\test.txt")){ Write-Host "ファイルが見つかりません。待機中です…" Start-Sleep -Seconds 1 } Write-Host "ファイルが見つかりました!" |
『while』の繰り返し処理を終了し、次の処理を実行させる方法
『while』の繰り返し処理を終了し、次の処理を実行したい(『while』ループを抜ける)場合には、繰り返し処理部で『break』と記述してください。
以下の処理は、先ほどご紹介したファイルが作成されるまで待機する処理と、同様の動作を行う処理を『break』を使って記述したものです。
1 2 3 4 5 6 7 8 | while ($true){ if (Test-Path -Path "C:\sample\test.txt"){ break } Write-Host "ファイルが見つかりません。待機中です…" Start-Sleep -Seconds 1 } Write-Host "ファイルが見つかりました!" |
catchされてしまう例外(エラー)が発生する処理のリトライ方法
PowerShellで記述した処理の中には、実行時にエラーが発生してしまう(『try catch』構文が記述されていた場合に、catch{}部の処理が実行されてしまう)ものもあります。
たとえば、ファイルにデータを追記する『Add-Content』コマンドレットは、実行時に他のプロセスが対象のファイルを開いていた場合などに、『System.IO.IOException』を発生させます。
そして併せて『-ErrorAction Stop』パラメーターを指定し、『try catch』構文が記述されていた場合には、catch{}部の処理が実行されます。
こういった、実行時にエラーが発生してしまう可能性がある処理の、エラー時リトライ処理を実装する場合には、『while』の繰り返し処理部に当該エラーをcatchする処理を記述。
そしてこのcatch{}部に、一定時間待つような処理を記述する方法が考えられます。
『C:\sample\test.txt』というパスに保存されているファイルに、『追記データです』というデータを追記。
追記の際、他のプロセスが対象のファイルを開いていたなどの理由により、『System.IO.IOException』が発生した場合には1秒おきにリトライする。※
というような処理を実装する場合には、たとえば以下のような方法で対応可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | try{ #リトライ時の間隔をミリ秒単位で指定する。 $RetryWaitMilliseconds = 1000 while ($true){ try{ "追記データです" | Add-Content -Path "C:\sample\test.txt" -ErrorAction Stop break }catch [System.IO.IOException]{ Write-Host "System.IO.IOExceptionが発生しました。" Start-Sleep -Milliseconds $RetryWaitMilliseconds } } }catch{ Write-Host "予期しないエラーが発生しました。" }finally{ Write-Host "finally部の処理です。" } |
※
上記スクリプトの例では、『while』の内部のtry{}部で『System.IO.IOException』クラス以外の例外が発生した場合にはリトライが行われず、予期しないエラーとして扱われます。
したがって実際の実装時には、その他の発生する可能性のある例外についての考慮も必要です。
参考:PowerShellで発生したエラーの型によって処理を分けたり型を指定してエラーを発生させる方法
PowerShellで無限ループを使って、リトライ回数に上限を設けて処理をリトライする方法
何らかの処理の完了を待つ(処理が完了するまでリトライする)ような実装をする際、リトライ回数の上限を設けたいケースもあるでしょう。
たとえば、最初に確認を行った際に失敗(処理が正常に完了していない状態)を検出。
その後3回確認処理をリトライし、3回目のリトライも失敗(初回の処理も含めると合計4回失敗)した場合には、それ以上リトライしない。
といったパターンですね。
無限ループを使ってこういったケースに対応する場合、指定回数のリトライを実行しても処理が正常に完了しない場合に『throw』を使って例外を発生させ、『while』の繰り返し処理を終了する。
また『throw』を使うのではなく、『break』を使って繰り返し処理を終了し、その後の処理(『while』ループの外の処理)で失敗時の対処を行う、といった対応方法が考えられます。
『C:\sample\test.txt』というパスに保存されているファイルに、『追記データです』というデータを追記。
追記の際、他のプロセスが対象のファイルを開いていたなどの理由により、『System.IO.IOException』が発生した場合には1秒おきにリトライする。
ただしリトライは最大3回まで実行し、3回目のリトライで失敗した場合には、『リトライ回数の上限に達したため、処理を中止しました。』というエラーをthrowする。
このような処理を実装するケースでは、たとえば以下のような記述により対応できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | try{ #リトライする回数を指定する。 #3を指定した場合、最初の1回 + 3回のリトライを行うため、最大4回処理の実行を行う。 $RetryCount = 3 #リトライ時の間隔をミリ秒単位で指定する。 $RetryWaitMilliseconds = 1000 while ($true){ try{ "追記データです" | Add-Content -Path "C:\sample\test.txt" -ErrorAction Stop break }catch [System.IO.IOException]{ Write-Host "System.IO.IOExceptionが発生しました。" if ($RetryCount -gt 0){ Start-Sleep -Milliseconds $RetryWaitMilliseconds $RetryCount-- }else{ throw "リトライ回数の上限に達したため、処理を中止しました。" } } } }catch{ Write-Host ("エラーメッセージ:" + $Error[0].Exception.Message) }finally{ Write-Host "finally部の処理です。" } |
参考:PowerShellで発生したエラーの型によって処理を分けたり型を指定してエラーを発生させる方法
以上、参考になさってくださーい!