Powershell이 ​​명령을 병렬로 실행할 수 있습니까?


125

여러 이미지에 대해 일괄 처리를 수행하는 powershell 스크립트가 있으며 병렬 처리를 수행하고 싶습니다. Powershell에는 start-job, wait-job 등과 같은 백그라운드 처리 옵션이있는 것 같지만 병렬 작업을 수행하기 위해 찾은 유일한 리소스는 스크립트 텍스트를 작성하고 실행하는 것뿐입니다 ( PowerShell Multithreading ).

이상적으로는 .net 4의 병렬 foreach와 유사한 것을 원합니다.

다음과 같이 꽤 보이지 않는 것 :

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

아마도 C #으로 내려가는 것이 더 나을 것입니다 ...


tl; dr : receive-job (wait-job ($a = start-job { "heyo!" })); remove-job $a 또는 작업이 완료되기 전에 $a = start-job { "heyo!" }; wait-job $a; receive-job $a; remove-job $a전화를 걸면 receive-job아무것도 얻지 못할 수도 있습니다.
Andrew

또한(get-job $a).jobstateinfo.state;
Andrew

답변:


99

백그라운드 작업을 사용하여 Powershell 2에서 병렬 작업을 실행할 수 있습니다 . Start-Job 및 기타 작업 cmdlet을 확인하십시오 .

# 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

3
그래서이 제안을 여러 번 시도했지만 내 변수가 올바르게 확장되지 않는 것 같습니다. 동일한 예를 사용하려면이 행이 실행될 때 : 현재 항목 Test-Path "\\$_\c$\Something"으로 확장 될 것으로 예상합니다 $_. 그러나 그렇지 않습니다. 대신 빈 값을 반환합니다. 이것은 스크립트 블록 내에서만 발생하는 것 같습니다. 첫 번째 댓글 직후에 그 값을 쓰면 제대로 작동하는 것 같습니다.
rjg 2011

1
@likwid-사이트에 대한 별도의 질문처럼 들립니다
Steve Townsend

백그라운드에서 실행중인 작업의 출력을 어떻게 볼 수 있습니까?
SimpleGuy

@SimpleGuy - - 출력 캡처에 대한 정보를 원하시면 여기를 참조 stackoverflow.com/questions/15605095/... - 당신이 백그라운드 작업이 완료 될 때까지 신뢰성이 볼 수있는 것처럼 보이지 않는다.
Steve Townsend

@SteveTownsend 감사합니다! 실제로보기 출력은 화면에서 그렇게 좋지 않습니다. 지연이 발생하므로 유용하지 않습니다. 대신 새 터미널 (쉘)에서 프로세스를 시작 했으므로 이제 각 프로세스가 서로 다른 터미널에서 실행되어 진행 상황을 훨씬 더 깔끔하게 볼 수 있습니다.
SimpleGuy

98

Steve Townsend의 대답은 이론적으로는 정확하지만 @likwid가 지적한 것처럼 실제로는 아닙니다. 내 수정 된 코드는 작업 컨텍스트 장벽 을 고려합니다 . 기본적으로 그 장벽을 넘는 것은 없습니다! 따라서 자동 $_변수는 루프에서 사용할 수 있지만 작업에 의해 생성 된 별도의 컨텍스트 내부에 있으므로 스크립트 블록 내에서 직접 사용할 수 없습니다.

부모 컨텍스트에서 자식 컨텍스트로 변수를 전달하려면 -ArgumentList매개 변수를 사용 Start-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 문서에 대한 참조를 제공하고 싶지만, 아쉽게도 제 검색은 결실이 없었습니다. 컨텍스트 분리가 문서화 된 위치를 아는 경우 여기에 의견을 게시하여 알려주세요!)


이 답변에 감사드립니다. 귀하의 솔루션을 사용해 보았지만 완전히 작동하지 못했습니다. 여기에서 내 질문을 살펴볼 수 있습니까? stackoverflow.com/questions/28509659/…
David는 Monica를 복원한다고 말합니다.

또는 별도의 스크립트 파일을 호출하는 것은 매우 쉽습니다. 그냥 사용Start-Job -FilePath script.ps1 -ArgumentList $_
차드 Zawistowski

다른 방법은 스크립트 생성의 예비 단계를 수행하는 것입니다. 여기서 변수 확장 외에는 수행되지 않고 생성 된 스크립트를 병렬로 호출합니다. 스크립트 생성을 지원하기위한 것은 아니지만 스크립트 생성에 적용 할 수있는 작은 도구가 있습니다. 여기에서 볼 수 있습니다 .
Walter Mitty

작동합니다. 하지만 ScriptBlock에서 라이브 피드 출력 스트림을 가져올 수 없습니다. 출력은 ScriptBlock이 반환 될 때만 인쇄됩니다.
vothaison

8

http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0

여러 스크립트 블록 / cmdlet / 함수를 동시에 실행할 수있는 invoke-async를 만들었습니다. 이는 소규모 작업 (100 대의 컴퓨터에 대한 서브넷 스캔 또는 wmi 쿼리)에 적합합니다. 실행 공간을 만드는 오버 헤드와 시작 작업의 시작 시간이 상당히 높기 때문입니다. 그렇게 사용할 수 있습니다.

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

단지 cmdlet / 함수

$servers = Get-Content servers.txt 

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

8

요즘 이것에 대한 많은 답변이 있습니다.

  1. 작업 (또는 PS 6/7 또는 모듈의 스레드 작업)
  2. 시작 프로세스
  3. 워크 플로우
  4. 다른 실행 공간이있는 powershell API
  5. 여러 컴퓨터에서 invoke-command (모두 localhost 일 수 있음) (관리자 여야 함)
  6. ISE의 다중 세션 (런 스페이스) 탭 또는 원격 powershell ISE 탭
  7. Powershell 7에는 foreach-object -parallel# 4에 대한 대안이 있습니다.

문자 그대로 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

다음은 runspaces 예제가있는 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

7

백그라운드 작업은 설정 비용이 많이 들고 재사용 할 수 없습니다. PowerShell MVP Oisin Grehan은 PowerShell 멀티 스레딩 의 좋은 예 입니다.

(2010 년 10 월 25 일 사이트가 다운되었지만 웹 아카이브를 통해 액세스 할 수 있음).

여기에서 데이터로드 루틴에 사용하기 위해 조정 된 Oisin 스크립트를 사용했습니다.

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


이 답변에 대한 링크 부패가 설정되었습니다
Luke

4

이전 답변을 완료하려면를 사용 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

0

Powershell 7에서는 ForEach-Object -Parallel을 사용할 수 있습니다.

$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
    "$using:Message $_"
} -ThrottleLimit 4

0

최신 크로스 플랫폼 powershell (btw해야 함) https://github.com/powershell/powershell#get-powershell을 사용하는 경우 단일 &을 추가 하여 병렬 스크립트를 실행할 수 있습니다 . ( ;순차 실행에 사용 )

제 경우에는 2 개의 npm 스크립트를 병렬로 실행해야했습니다. npm run hotReload & npm run dev


powershell스크립트 에 사용할 npm을 설정할 수도 있습니다 (기본적 cmd으로 Windows에서 사용).

프로젝트 루트 폴더에서 실행 npm config set script-shell pwsh --userconfig ./.npmrc 한 다음 단일 npm 스크립트 명령을 사용합니다.npm run start

"start":"npm run hotReload & npm run dev"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.