Powershell có thể chạy các lệnh song song không?


125

Tôi có một tập lệnh powershell để thực hiện một số xử lý hàng loạt trên một loạt các hình ảnh và tôi muốn thực hiện một số xử lý song song. Powershell dường như có một số tùy chọn xử lý nền như khởi động công việc, chờ đợi công việc, vv, nhưng chỉ nguồn lực tốt tôi thấy để làm việc song song đang viết nội dung của một kịch bản ra ngoài và chạy những người ( PowerShell Multithreading )

Lý tưởng nhất, tôi muốn một cái gì đó giống như tìm kiếm song song trong .net 4.

Một cái gì đó khá dường như:

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

Có lẽ tôi sẽ tốt hơn nếu chỉ rơi xuống c # ...


tl; dr: receive-job (wait-job ($a = start-job { "heyo!" })); remove-job $a hoặc $a = start-job { "heyo!" }; wait-job $a; receive-job $a; remove-job $aLưu ý rằng nếu bạn gọi receive-jobtrước khi công việc kết thúc, bạn có thể không nhận được gì cả.
Andrew

Ngoài ra(get-job $a).jobstateinfo.state;
Andrew

Câu trả lời:


99

Bạn có thể thực hiện các công việc song song trong Powershell 2 bằng cách sử dụng Công việc nền . Kiểm tra Start-Job và các lệnh ghép ngắn công việc khác.

# 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
Vì vậy, tôi đã thử đề xuất này nhiều lần, nhưng dường như các biến của tôi không được mở rộng chính xác. Để sử dụng cùng một ví dụ, khi dòng này thực thi: Test-Path "\\$_\c$\Something"Tôi hy vọng nó sẽ mở rộng $_sang mục hiện tại. Tuy nhiên, nó không. Thay vào đó, nó trả về một giá trị trống. Điều này dường như chỉ xảy ra từ trong khối kịch bản. Nếu tôi viết giá trị đó ra ngay sau bình luận đầu tiên, nó dường như hoạt động chính xác.
rjg

1
@likwid - nghe có vẻ như một câu hỏi riêng cho trang web
Steve Townsend

Làm cách nào tôi có thể xem đầu ra của công việc đang chạy trong nền?
SimpleGuy

@SimpleGuy - xem tại đây để biết thông tin về chụp đầu ra - stackoverflow.com/questions/15605095/ mẹo - dường như bạn không thể xem điều này một cách đáng tin cậy cho đến khi công việc nền hoàn thành.
Steve Townsend

@SteveTownsend Cảm ơn! Trên thực tế xem đầu ra là một không tốt trên màn hình. Đi kèm với sự chậm trễ, vì vậy không hữu ích cho tôi. Thay vào đó, tôi bắt đầu một quy trình trên thiết bị đầu cuối mới (shell), vì vậy bây giờ mỗi quy trình đang chạy trên thiết bị đầu cuối khác nhau, cho phép xem tiến trình tốt hơn và sạch hơn nhiều.
SimpleGuy

98

Câu trả lời từ Steve Townsend là đúng trong lý thuyết nhưng không thực tế như @likwid đã chỉ ra. Mã sửa đổi của tôi tính đến rào cản bối cảnh công việc - mặc định vượt qua rào cản đó! $_Do đó, biến tự động có thể được sử dụng trong vòng lặp nhưng không thể được sử dụng trực tiếp trong khối tập lệnh vì nó nằm trong một bối cảnh riêng được tạo bởi công việc.

Để chuyển các biến từ bối cảnh cha sang bối cảnh con, sử dụng -ArgumentListtham số trên Start-Jobđể gửi nó và sử dụng parambên trong khối tập lệnh để nhận nó.

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 *

(Tôi thường muốn cung cấp tài liệu tham khảo cho tài liệu PowerShell làm bằng chứng hỗ trợ nhưng, than ôi, tìm kiếm của tôi không có kết quả. Nếu bạn tình cờ biết được phân tách ngữ cảnh ở đâu, hãy đăng bình luận tại đây để cho tôi biết!)


Cảm ơn câu trả lời này. Tôi đã thử sử dụng giải pháp của bạn, nhưng tôi không thể làm cho nó hoạt động hoàn toàn. Bạn có thể xem câu hỏi của tôi ở đây không: stackoverflow.com/questions/28509659/ Khăn
David nói Phục hồi lại

Ngoài ra, thật dễ dàng để gọi một tệp script riêng biệt. Chỉ cần sử dụngStart-Job -FilePath script.ps1 -ArgumentList $_
Chad Zawistowski

Một cách tiếp cận khác là thực hiện một bước sơ bộ của việc tạo tập lệnh, trong đó không có gì được thực hiện ngoài việc mở rộng biến, và sau đó gọi các tập lệnh được tạo song song. Tôi có một công cụ nhỏ có thể phù hợp với việc tạo tập lệnh, mặc dù nó không bao giờ có nghĩa là hỗ trợ tạo tập lệnh. Bạn có thể thấy nó ở đây .
Walter Mitty

Những công việc này. Nhưng tôi không thể nhận được luồng đầu ra của nguồn cấp dữ liệu trực tiếp từ ScriptBlock. Đầu ra chỉ được in khi ScriptBlock trả về.
vothaison

8

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

tôi đã tạo một invoke-async cho phép bạn chạy nhiều khối script / cmdlets / hàm cùng một lúc. điều này rất tốt cho các công việc nhỏ (quét mạng con hoặc truy vấn wmi đối với 100 máy) vì chi phí để tạo không gian chạy so với thời gian khởi động của công việc bắt đầu khá quyết liệt. Nó có thể được sử dụng như vậy.

với 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

chỉ cmdlet / hàm

$servers = Get-Content servers.txt 

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

8

Có rất nhiều câu trả lời cho những ngày này:

  1. công việc (hoặc threadjobs trong PS 6/7 hoặc mô-đun)
  2. bắt đầu quá trình
  3. quy trình công việc
  4. api powershell với một không gian khác
  5. lệnh invoke với nhiều máy tính, tất cả đều có thể là localhost (phải là quản trị viên)
  6. nhiều tab (runspace) trong ISE hoặc các tab ISE quyền hạn từ xa
  7. Powershell 7 có một foreach-object -parallelsự thay thế cho # 4

Đây là quy trình làm việc với nghĩa đen là một foreach:

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

work

3 done
1 done
2 done

Hoặc một quy trình làm việc với một khối song song:

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

Đây là một api với ví dụ về không gian chạy:

$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

Công việc nền rất tốn kém để thiết lập và không thể tái sử dụng. PowerShell MVP Oisin Grehan có một ví dụ điển hình về đa luồng PowerShell.

(25/10/2010 trang web ngừng hoạt động, nhưng có thể truy cập qua Lưu trữ web).

Tôi đã sử dụng tập lệnh Oisin được điều chỉnh để sử dụng trong quy trình tải dữ liệu tại đây:

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


Liên kết thối đã được thiết lập cho câu trả lời này
Luke

4

Để hoàn thành các câu trả lời trước đó, bạn cũng có thể sử dụng Wait-Jobđể chờ tất cả các công việc hoàn thành:

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

Trong Powershell 7, bạn có thể sử dụng ForEach-Object -Parallel

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

0

Nếu bạn đang sử dụng powershell đa nền tảng mới nhất (mà bạn nên btw) https://github.com/powershell/powershell#get-powershell , bạn có thể thêm một &tập lệnh để chạy các tập lệnh song song. (Sử dụng ;để chạy tuần tự)

Trong trường hợp của tôi, tôi cần chạy song song 2 npm script: npm run hotReload & npm run dev


Bạn cũng có thể thiết lập npm để sử dụng powershellcho các tập lệnh của nó (theo mặc định nó sử dụng cmdtrên windows).

Chạy từ thư mục gốc của dự án: npm config set script-shell pwsh --userconfig ./.npmrc và sau đó sử dụng lệnh script npm duy nhất:npm run start

"start":"npm run hotReload & npm run dev"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.