Home >
Windows にまつわる e.t.c.
リモート コンピューターのパラレル バッチ操作(Invoke-Command -AsJob)
リモートコンピューター台数が少ない場合は、Invoke-Command
でシーケンシャル処理でも構わないのですが、処理対象のリモートコンピューターが100台以上あるとか、1台当たりの処理が長時間に及ぶ場合は時間短縮のためにパラレルに処理を実行したくなります。
このパラレル処理をするのが Invoke-Command の -AsJob オプションです。
まずは試しに dir c:\ を -AsJob で投入してみましょう。
PS C:\> $Return = Invoke-Command log -ScriptBlock { dir c:\ } -AsJob
PS C:\> echo $Return
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
33 Job33 RemoteJob Running True TergetServer dir c:\
|
Invoke-Command は即座に終了し、戻り値にジョブの実行状態が格納されます。
実行状態は Get-Job で確認できます。
PS C:\> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
33 Job33 RemoteJob Completed True TergetServer dir c:\
|
処理状態を表す State には以下の種類があります(これ以外にあるかも)
Running |
実行中 |
Completed |
正常完了 |
Failed |
異常終了 |
Disconnected |
切断された |
余談ですが、Invoke-Command -AsJob でリモートコンピューターを再起動すると、再起動時に State が
Completed になります。
Completedしたジョブの処理結果は Receive-Job で受け取ります。(Invoke-Command
そのものと、戻り値ハンドリングはこちらをどうぞ。-AsJob
はオプションなので、そのまま応用できます)
PS C:\> $Return = Receive-Job -Id 33
PS C:\> echo $Return
ディレクトリ: C:\
Mode LastWriteTime Length Name PSComputerName
---- ------------- ------ ---- --------------
da--- 2014/01/25 14:33 CheckEventLog TergetServer
d---- 2013/09/12 16:17 inetpub TergetServer
d---- 2014/12/01 12:08 LogMove TergetServer
d---- 2013/08/23 0:52 PerfLogs TergetServer
d---- 2014/12/01 12:14 ping_log TergetServer
d-r-- 2015/02/15 10:23 Program Files TergetServer
d---- 2015/02/15 10:23 Program Files (x86) TergetServer
d---- 2014/12/01 12:10 pSyslog TergetServer
d---- 2014/10/10 10:21 Tools TergetServer
d-r-- 2013/09/12 16:23 Users TergetServer
d---- 2015/03/12 4:37 Windows TergetServer
d---- 2013/09/12 16:15 Windows.old TergetServer
d---- 2015/03/15 12:32 WindowsUpdate TergetServer
d---- 2014/12/01 12:04 work TergetServer
d---- 2015/03/15 12:25 WU_Log TergetServer
|
Receive-Job で結果を取り出せるのは1度きりなので、もう一度 Get-Job すると、HasMoreData が
False に変わり、戻りデーターがもう無いことがわかります。
PS C:\> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
33 Job33 RemoteJob Completed False TergetServer dir c:\
|
処理が終わっても、ジョブは残っているので Remove-Job でジョブを削除します。
実行中のジョブを強制終了するには Stop-Job コマンドレットを使います
Job を Failed にする
-AsJob で投入したジョブは、return
で成否等の戻り値を返して制御をする事が多いと思いますが、致命的なエラーが発生した場合は強制的に Job を Failed にすることが出来ます。
Job を Failed する場合は、例外を throw します。
例外発生時のデフォルト動作は「処理継続」なので、異常終了させるために $ErrorActionPreference に
"Stop" をセットして処理停止にします。
# 例外発生時動作終了 $ErrorActionPreference = "Stop"
$Message =
"○○異常" throw $Message |
例外発生時の動作をデフォルトに戻すには、明示的に $ErrorActionPreference に "Continue"
をセットする必要があります。
実行状態監視例
こんな感じでループまわして状態監視することが出来ます。
# -AsJob 投入した ジョブの状態確認
do{
# ジョブ取得
$Jobs = Get-Job
# 実行中ジョブ
$RunningJobs = $Jobs | ?{ $_.State -eq "Running"}
# 正常終了したジョブ
$CompletedJobs = $Jobs | ?{ $_.State -eq "Completed"}
if( $CompletedJobs -ne $null ){
foreach( $Job in $CompletedJobs ){
$Location = $Job.Location
# 戻り値取得
$Echos = Receive-Job -Id $Job.Id
$Return = $Echos[$Echos.Lenght - 1]
# エラー(戻り値に $true/$false を返す仕様の場合)の場合は標準出力を echo
if( $Return -ne $true ){
echo "[ERROR] $Location Error"
echo "[ERROR] ------------ $Location stdout echo Start ------------"
foreach( $Echo in $Echos ){
echo $Echo
}
echo "[ERROR] ------------ $Location stdout echo End ------------"
}
else{
echo "[INFO] $Location Completed"
}
# Job 削除
Remove-Job -Id $Job.Id
}
}
# 残っているジョブ状態を表示(10秒間隔)
$AllJobs = get-job
if( $AllJobs -ne $null ){
$Now = Get-Date
$Message = "未処理ジョブ " + $Now
echo $Message
$AllJobs | Format-Table -Property Id,Name,State,Location -AutoSize | Out-Host
echo ""
echo ""
echo ""
sleep 10
}
}
while( $RunningJobs -ne $null )
# 異常終了したジョブ
$FailedJobs = Get-Job | ?{ $_.State -eq "Failed"}
if( $FailedJobs -ne $null ){
# 一覧表示
echo "Fail Job"
$FailedJobs | Format-Table -Property Id,Name,State,Location -AutoSize | Out-Host
echo ""
# 異常終了した Job を処理
foreach( $Job in $FailedJobs ){
$Location = $Job.Location
# 標準出力と戻り値を echo して Job 削除
echo "[FAIL] $Location Failed"
echo "[FAIL] ------------ $Location stdout echo Start ------------"
$Failechos = Receive-Job -Id $Job.Id
if( $Failechos -ne $null ){
foreach( $Failecho in $Failechos ){
echo $Failecho
}
}
echo "[FAIL] ------------ $Location stdout echo End ------------"
Remove-Job -Id $Job.Id
}
}
# 切断されたジョブ(ジョブ強制削除)
$DisconnectedJobs = Get-Job | ?{ $_.State -eq "Disconnected"}
if( $DisconnectedJobs -ne $null ){
# 一覧表示
echo "Disconnect Job"
$DisconnectedJobs | Format-Table -Property Id,Name,State,Location -AutoSize | Out-Host
echo ""
# Job 強制削除
foreach( $Job in $DisconnectedJobs ){
Stop-Job -Id $Job.Id
Remove-Job -Id $Job.Id
}
}
|
Invoke-Command -AsJob の注意点は、投入元の PowerShell
プロンプトを閉じたり、コンピューターの再起動してしまうと、セッションが切断され Get-Job でジョブを捕まえる事が出来なくなる点です。
この点を注意すれば、Invoke-Command -AsJob はパラレルに大量のジョブを投入でき、ループで回して戻り値で細かく制御することも可能なのでお勧めです。
# 同一カテゴリー
リモート
コンピューターの対話操作(Enter-PSSession)
リモート
コンピューターのバッチ操作(Invoke-Command)
Copyright © MURA
All rights reserved.