Windows PowerShellでバックグラウンドタスクを扱う
ASCII.jp / 2023年12月24日 10時0分
実行時間の長いコマンドを使うとき、実行している間に別の作業をしたくなることがある。WindowsはGUIなので、別のコンソールウィンドウを開けば、別のセッションとしてシェルが起動するので、基本はこれで間に合う。
しかし、今のセッションと同じ環境でコマンドを使いたいといった場合は、この方法は使えない。たとえば、すでに変数などを使って何かの作業をしているときなど、別のセッション(ウィンドウ)では実行環境が異なる。こうした場合、PowerShellにはバックグラウンド実行という方法がある。
まずは簡単な例を紹介
まずは簡単な例を使って、バックグラウンド実行を使ってみよう。たとえば、dirコマンド(get-childItemコマンドのエイリアス)をバックグラウンドで実行させてみる。簡易な方法としてバックグラウンド演算子“&”を使う方法がある。これは、Linuxのbashなどと同じ記法だ。ただし、この記法は、Windows標準搭載のWindows PowerShell(Ver.5.1)では利用できない。PowerShell Ver.6以降が必要となる。
dir &
このときのコマンドの出力は、dirコマンドの出力ではなく、ジョブ(Job)オブジェクトになる。
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661234/x/99610b6b0d8cf63d.png)
これは略記法で、正式にはJob関連のコマンドのStart-Jobコマンドでする(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/start-job?view=powershell-7.4)。
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661238/x/ce44624ee2db7d56.png)
この場合、実行させるコマンドを波括弧「{ }」でくくってスクリプトブロックにする必要がある。
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661235/x/6f58c85f640b6ffb.png)
以降、このスクリプトブロックやバックグラウンド演算子を使ったコマンドラインを「ジョブコマンド」と表記する。
start-job -ScriptBlock { dir }
なお、「-ScriptBlock」オブションは省略して「start-job {dir}」とすることもできる。また、エイリアスとしてsajbが定義されているので「sajb {dir}」でも可能だ。
実際に時間が掛かるコマンドをバックグラウンド処理してみよう。たとえば、Windows Updateの現在の状態を取得するには少し時間がかかる。
((New-Object -ComObject Microsoft.Update.Session).CreateUpdateSearcher()).Search("IsInstalled=0 OR IsInstalled=1").updates &
ジョブコマンドの実行状態を調べるには、Get-Jobを使う(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/get-job)。ジョブを起動したときに表示されるIdを覚えているなら、それを使って特定のJobの情報のみを「Get-Job -Id <ジョブのID>」で表示できる。何も指定しなければ、すべてのジョブが表示される。
なお、ジョブオブジェクトは、起動したらジョブコマンドの実行が終了しても、残ったままになる。不要ならRemove-Jobで削除することができる(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/remove-job)。
ジョブコマンドの出力を受け取るにはReceive-Jobコマンドを使う(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/receive-job)。このとき、ジョブのIDを指定する必要がある。Receive-Jobの実行結果を変数に代入することで、ジョブコマンドの出力を変数に入れることができる。
ジョブコマンドが終了するまで待つには、Wait-Jobコマンドを使う(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/wait-job)。
コマンドをJobとして起動すると、実行時間が長いコマンドであっても、すぐにプロンプトに戻ってくる。Jobは別プロセスとしてPowerShellを起動し、その中で指定したコマンドを実行する。単純なPowerShellコマンドや、外部コマンド(Windowsに付属のExe実行形式ファイルになっているコマンド)の実行には問題はないが、別プロセスとなるため、現在の環境が持つ変数や関数などを使うことはできない。この点には注意が必要だ。
そもそもジョブとは何?
コマンドをバックグラウンドで実行するとき、PowerShellでは、「ジョブ」オブジェクトを使って、バックグラウンド実行を表現する。コマンドをバックグラウンド実行すると、ジョブオブジェクトが作られ、このオブジェクトに対して操作を行うことで、実行を停止させる、あるいは状態を調べることができる。ジョブの状態はStateプロパティが表わす。
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661239/x/2567160644a67155.png)
Stateプロパティを確認するにはGet-Jobコマンドを使う。なお、バックグラウンドジョブは、停止させることは可能だが、中断させることや停止したジョブを再開させることはできない。Running、Completed以外の場合、実行が失敗あるいは他の理由でバックグラウンドジョブは停止している。再実行させるには同じStart-Jobコマンドを実行するしかない。
バックグラウンド演算子やStart-Jobコマンドで起動するジョブを「Background Job」という。これは複数あるPowerShellジョブの1つだ。
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661240/x/f52a33f2d7d972b9.png)
そのほか、WorkflowやCIMJobなどもあるが、特定目的のオブジェクト処理に関するものであり、ここでは解説しない。スケジュールジョブは、タスクスケジューラーのタスクをPowerShellのジョブとして扱うものだ。ここでは、バックグラウンドジョブとスレッドジョブのみを扱う。
ジョブは、実行中のPowerShellとは別プロセスで起動されるため、ジョブコマンドからは実行中のPowerShellにある変数や関数などにアクセスできない。同様にジョブコマンド側で変数に定義しても、PowerShell側に持ち込むこともできない。
関数定義や変数へのアクセスが必要になる場合、-InitializationScriptオプションや-ArgumentListオプションを使って必要な情報を渡す。-InitializationScriptオプションを使うと、ジョブコマンドで利用できる関数を定義することができる。
Start-Job -InitializationScript {function mydir($x){ Get-ChildItem $x }} { mydir "D:\temp" }
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661236/x/4a60b082cbecb0f0.png)
-ArgumentListオプションを使うことで、ジョブコマンドに引数を与えることができる。
![Windows PowerShellでバックグラウンドタスクを扱う](https://ascii.jp/img/2023/12/23/3661237/x/0f4044b295f98c69.png)
ただし、スクリプトブロック内でParmステートメントを使って受け取る引数を定義しておく必要がある。
$mypath="D:\temp" start-job -ScriptBlock { param($p); dir $p } -ArgumentList $mypath
実行結果を受け取る場合には、ジョブコマンドは代入文とせず実行するだけにしてReceive-Jobコマンドの出力を変数に代入する。ただし、-keepオプションを付けないで実行するとその時点の実行結果は消えてしまう。これは、長い実行時間の間に散発的に出力を行うようなコマンドの場合、-keepオプションを付けないReceive-Jobを繰り返すことで最新の出力のみを受け取り可能にするためだ。
Start-Job -ScriptBlock { dir } $r=Receive-Job -id <ジョブID>
バックグラウンドジョブが終了し、結果も受け取って用済みとなったときには、Remove-Jobで削除することができる。すべてのジョブを削除したいときには、
Get-Job | Remove-Job
とする。
スレッドジョブ
スレッドジョブは、現在のPowerShellと同じプロセス内で、スレッドとして実行される。バックグラウンドジョブは、別プロセスを起動するため起動に時間がかかり、メモリなどのリソースを消費する。大量のジョブを並列実行させたときには、起動時間のオーバーヘッドが大きくなる。
スレッドジョブは、プロセスを生成しないので高速に起動する。しかし、スレッドジョブはジョブコマンド側のエラーが実行中のPowerShellに影響を与える可能性があり、最悪、ジョブコマンドとともにクラッシュする可能性がある。
速度を取るか、安全性を取るかといった問題だが、単純な外部コマンドなどの実行であれば、起動のオーバーヘットはそれほど問題にならないので、わざわざスレッドジョブを使う必要はないだろう。スレッドジョブは、クラッシュする危険がないコマンドを大量に並行実行させるときに使う。
スレッドジョブは、起動にStart-ThreadJobコマンド(https://learn.microsoft.com/ja-jp/powershell/module/threadjob/start-threadjob)を用いる以外は、バックグラウンドジョブと同じで、他のバックグラウンドジョブ用のコマンド(Get-Jobなど)をそのまま使う。また、起動オプションに関しても共通して使えるものがある。
ただし、PowerShell環境としては分離されている(PowerShellのRunspaceを使っている)ため、バックグラウンドジョブと同じく、実行中のPowerShellで定義した関数や変数をジョブコマンドで直接利用できない。Start-Jobと同じく-InitializationScriptオプションや、-ArgumentListオプションを使って必要な情報を渡す。
かなり時間のかかるUpdate-Helpコマンド(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/update-help)などもバックグラウンドで処理すれば終了を待つ必要もない。
PowerShellのバックグラウンド実行には制限も多いが、内部コマンドだけ、あるいは外部コマンドなら、あまり気にする必要はない。自作の関数があるときや変数でパラメーターを指定しているときのみ注意すればよい。
この記事に関連するニュース
-
Windows 11、更新プログラム(KB5039302)により繰り返し再起動する問題発生
マイナビニュース / 2024年6月30日 17時16分
-
Windowsが今更(?)開発者に優しくなろうとしている!? 「Dev Home」は開発者にとって使い物になる?
ASCII.jp / 2024年6月23日 10時0分
-
Microsoft、2024年6月の月例更新 - 49件の脆弱性への対応が行われる
マイナビニュース / 2024年6月12日 16時9分
-
Windows Subsystem for Linuxガイド 第36回 WSL2でDockerを使う その2「Dockerエンジン編」
マイナビニュース / 2024年6月10日 17時57分
-
Windows Terminal Preview v1.21では、前回終了時のタブとその表示内容を復元できるように
ASCII.jp / 2024年6月9日 10時0分
ランキング
-
1Google検索も不要に? 検索AI「Perplexity」がスゴすぎてちょっと怖い
ITmedia NEWS / 2024年7月5日 19時16分
-
2プリングルズ価格改定 ショート缶は容量そのままサイズ変更して空間率減少へ
ねとらぼ / 2024年7月5日 16時20分
-
3「コレのせいやったんか」 最近スマホが重い → “とんでもない理由”が判明……!? 「自分もやってた」「4年以上経ちました」と猛者たち集結
ねとらぼ / 2024年7月5日 16時0分
-
4清春、27年前にブチギレた“因縁の大物芸人”と対峙 トガっていた時期に頭たたかれ“殺しのリスト”入りへ「テレビナメるな」「パーンって」
ねとらぼ / 2024年7月5日 16時5分
-
5ランサムウェア被害、報告続出 イセトーサイバー攻撃で今わかっていること(7月5日時点)
ASCII.jp / 2024年7月5日 15時45分
記事ミッション中・・・
記事にリアクションする
![](/pc/img/mission/mission_close_icon.png)
記事ミッション中・・・
記事にリアクションする
![](/pc/img/mission/point-loading.png)
エラーが発生しました
ページを再読み込みして
ください
![](/pc/img/mission/mission_close_icon.png)