web-dev-qa-db-ja.com

Powershellはコマンドを並行して実行できますか?

大量の画像に対してバッチ処理を行うためのPowerShellスクリプトがあり、並列処理を行いたいのですが。 Powershellには、開始ジョブ、待機ジョブなどのバックグラウンド処理オプションがいくつかあるようですが、並列作業を行うために見つけた唯一の良いリソースは、スクリプトのテキストを書き出して実行することでした( PowerShell Multithreading

理想的には、.net 4の並列foreachに似たものが欲しいです。

次のようにかなり見えないもの:

foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
   .. Do Work
}

たぶんC#にドロップダウンしたほうがいいだろう...

108
Alan Jackson

Background Jobs を使用して、Powershell 2で並列ジョブを実行できます。 Start-Job およびその他のジョブコマンドレットを確認してください。

# Loop through the server list
Get-Content "ServerList.txt" | %{

  # Define what each job does
  $ScriptBlock = {
    param($pipelinePassIn) 
    Test-Path "\\$pipelinePassIn\c`$\Something"
    Start-Sleep 60
  }

  # Execute the jobs in parallel
  Start-Job $ScriptBlock -ArgumentList $_
}

Get-Job

# Wait for it all to complete
While (Get-Job -State "Running")
{
  Start-Sleep 10
}

# Getting the information back from the jobs
Get-Job | Receive-Job
85
Steve Townsend

Steve Townsendからの答えは理論的には正しいが、@ likwidが指摘したように実際にはそうではない。修正したコードでは、job-context barrier-デフォルトでは何もその壁を越えません!したがって、自動$_変数はループで使用できますが、ジョブによって作成された別のコンテキスト内にあるため、スクリプトブロック内で直接使用することはできません。

親コンテキストから子コンテキストに変数を渡すには、-ArgumentListStart-Jobパラメーターを使用して送信し、スクリプトブロック内でparamを使用して受信します。

cls
# Send in two root directory names, one that exists and one that does not.
# Should then get a "True" and a "False" result out the end.
"temp", "foo" | %{

  $ScriptBlock = {
    # accept the loop variable across the job-context barrier
    param($name) 
    # Show the loop variable has made it through!
    Write-Host "[processing '$name' inside the job]"
    # Execute a command
    Test-Path "\$name"
    # Just wait for a bit...
    Start-Sleep 5
  }

  # Show the loop variable here is correct
  Write-Host "processing $_..."

  # pass the loop variable across the job-context barrier
  Start-Job $ScriptBlock -ArgumentList $_
}

# Wait for all to complete
While (Get-Job -State "Running") { Start-Sleep 2 }

# Display output from all jobs
Get-Job | Receive-Job

# Cleanup
Remove-Job *

(私は一般的に、PowerShellドキュメントへの参照を証拠として提供したいと思いますが、残念ながら、私の検索は無益です。コンテキスト分離がどこでドキュメント化されているかを知りたければ、ここにコメントを投稿してください!)

89
Michael Sorens

http://gallery.technet.Microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f

複数のスクリプトブロック/コマンドレット/関数を同時に実行できるinvoke-asyncを作成しました。これは小さなジョブ(サブネットスキャンまたは数百台のマシンに対するwmiクエリ)に最適です。実行スペースを作成するためのオーバーヘッドとstart-jobの起動時間は非常に大きいためです。そのように使用できます。

scriptblockを使用すると、

$sb = [scriptblock] {param($system) gwmi win32_operatingsystem -ComputerName $system | select csname,caption} 

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $server -SetParam system  -ScriptBlock $sb

ちょうどコマンドレット/機能

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50
8
jrich523

バックグラウンドジョブはセットアップに費用がかかり、再利用できません。 PowerShell MVP Oisin Grehanには、PowerShellマルチスレッドの 良い例 があります。

(2010/10/25サイトはダウンしていますが、Webアーカイブ経由でアクセス可能です)。

ここで、データロードルーチンで使用するために、適応されたOisinスクリプトを使用しました。

http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1

7
Chad Miller

以前の回答を完了するには、Wait-Jobを使用して、すべてのジョブが完了するのを待つこともできます。

For ($i=1; $i -le 3; $i++) {
    $ScriptBlock = {
        Param (
            [string] [Parameter(Mandatory=$true)] $increment
        )

        Write-Host $increment
    }

    Start-Job $ScriptBlock -ArgumentList $i
}

Get-Job | Wait-Job | Receive-Job
3
Thomas

最近、これには非常に多くの答えがあります。

  1. ジョブ(またはPS 6またはモジュールのスレッドジョブ)
  2. 開始プロセス
  3. ワークフロー
  4. 別の実行スペースを持つpowershell api
  5. 複数のコンピューターでinvoke-commandを実行します。これらはすべてローカルホストにすることができます(管理者である必要があります)
  6. iSEの複数のセッションタブ

文字通りforeach -parallelを使用したワークフローは次のとおりです。

workflow work {
  foreach -parallel ($i in 1..3) { 
    sleep 5 
    "$i done" 
  }
}

work

3 done
1 done
2 done

または、並列ブロックを使用したワークフロー:

function sleepfor($time) { sleep $time; "sleepfor $time done"}

workflow work {
  parallel {
    sleepfor 3
    sleepfor 2
    sleepfor 1
  }
  'hi'
}

work 

sleepfor 1 done
sleepfor 2 done
sleepfor 3 done
hi

実行スペースを使用したAPIの例を次に示します。

$a =  [PowerShell]::Create().AddScript{sleep 5;'a done'}
$b =  [PowerShell]::Create().AddScript{sleep 5;'b done'}
$c =  [PowerShell]::Create().AddScript{sleep 5;'c done'}
$r1,$r2,$r3 = ($a,$b,$c).begininvoke() # run in background
$a.EndInvoke($r1); $b.EndInvoke($r2); $c.EndInvoke($r3) # wait
($a,$b,$c).streams.error # check for errors
($a,$b,$c).dispose() # clean

a done
b done
c done
1
js2010