最近在寫一些powershell腳本時候遇到一個問題,那就是要解壓十幾個zip文件,這樣僅執行完解壓操作差不多5min的時間就過去了,嚴重影響了效率,這時就想到了使用多線程的方法來執行這個解壓操作,經過學習了解到powershell提供了一個Start-Job命令來實現並行執行。接下來對這個命令做一個總結。
一、真實案例
以我之前遇到的問題作為示例來介紹這個命令,先貼出源方案,串行執行。c:\zipfile目錄下有十幾個zip文件,需要對文件進行解壓,按照原有的方案是遍歷整個目錄,一次解壓,耗時5min。
foreach ( $i in ls c:\zipfile\*.zip)
{
.\7z.exe x $i
}
利用start-job后的代碼:
foreach ( $i in ls c:\zipfile\*.zip)
{
$command = ".\7z.exe x $i"
Start-Job -ScriptBlock{ $command } #注意:在{}中不可以傳入“.\7z.exe x $i”類似這樣命令與變量的結合體,{}接收的是類似$command這樣的字符串或者是“.\7z.exe x a.zip”這樣的內容。否則會報錯哦
}
使用並行執行的方案后,執行時長縮小到了1min。
二、熟練使用start-job及相關配套命令
1、start-job
后台運行sleep命令,並且為這個任務命名為“sleep”。
Start-Job -ScriptBlock { sleep 10 } -Name sleep
在后台運行命令前首先將1賦值給$a,然后再去執行輸出$a的操作,最后命名為test,-InitializationScript參數的作用是任務開始前需要執行的。
Start-Job -InitializationScript { $a = 1 } -ScriptBlock { echo $a } -Name test
若需要后台執行腳本,使用-FilePath參數即可
Start-Job -FilePath .\a.ps1
2、get-job
獲取當前所有后台進程的狀態信息,starte是任務的執行狀態,“Completed”表示任務完成,“Failed”表示任務執行失敗,“Running”表示正在運行的,“Stopped”表示任務停止。
PS C:\> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
29 Job29 BackgroundJob Completed True localhost mkdir c:\a\b...
31 test1 BackgroundJob Failed False localhost slepp 20
3、stop-job
停止后台運行的某個任務,下面的例子是后台運行一個睡20s的任務,得知該任務的id是43,然后停止id=43的任務,再去查看后台任務。如果知道后台任務的名稱,也可以根據名字來停止(參數是-Name)
PS C:\Users\bill> Start-Job -ScriptBlock{ sleep 20}
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
43 Job43 BackgroundJob Running True localhost sleep 20
PS C:\Users\bill> Stop-Job -id 43
PS C:\Users\bill> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
43 Job43 BackgroundJob Stopped False localhost sleep 20
4、receive-job
我們在使用start-job后台執行任務的時候,往往是不會顯示命令的執行結果,此時可以使用receive-job命令查看它的結果
PS C:\Users\bill> Start-Job -ScriptBlock { echo "1" } -Name "ok"
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
45 ok BackgroundJob Running True localhost echo "1"
PS C:\Users\bill> Receive-Job -Name ok
1
5、wait-job
再回到最初的案例,對文件解壓完后,需要將各個解壓文件移動到某個位置,但是使用start-job將每個解壓任務放到后台,此時系統會接着執行后續的操作,這時出現了文件還沒有解壓完成,就已經開始對解壓文件進行操作,導致腳本異常。針對這個問題需要用到wait-job命令。
PS C:\> Start-Job -ScriptBlock { sleep 5 } -Name s1
Start-Job -ScriptBlock { sleep 6 } -Name s2
Start-Job -ScriptBlock { sleep 7 } -Name s3
echo "satrt"
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
47 s1 BackgroundJob Running True localhost sleep 5
49 s2 BackgroundJob Running True localhost sleep 6
51 s3 BackgroundJob Running True localhost sleep 7
satrt
我們可以看到三個后台任務和輸出“start”幾乎是同時執行的,加入wait-job后
PS C:\> Start-Job -ScriptBlock { sleep 5 } -Name s1
Start-Job -ScriptBlock { sleep 6 } -Name s2
Start-Job -ScriptBlock { sleep 7 } -Name s3
Wait-Job *
echo "satrt"
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
53 s1 BackgroundJob Running True localhost sleep 5
55 s2 BackgroundJob Running True localhost sleep 6
57 s3 BackgroundJob Running True localhost sleep 7
57 s3 BackgroundJob Completed False localhost sleep 7
55 s2 BackgroundJob Completed False localhost sleep 6
53 s1 BackgroundJob Completed False localhost sleep 5
satrt
加入wait-job命令后的現象是,等待所有的后台命令執行完成后,才去執行echo “start”。此時就明白了wait-job的作用了,那就是等待執行的任務完成后再去執行其他的。
其中wait-job中-Id和-Name可以指定特定的任務。
若不用wait-job命令可以下面的代碼來監控后台任務是否執行完
Remove-Job *
#測試計時開始
$start_time = (Get-Date)
Start-Job -ScriptBlock { sleep 9; Write-Host "Hello myJob1."; } -Name "myJob1"
Start-Job -ScriptBlock { sleep 5; Write-Host "Hello myJob2."; } -Name "myJob2"
$taskCount = 2
while($taskCount -gt 0)
{
foreach($job in Get-Job)
{
$state = [string]$job.State
if($state -eq "Completed")
{
Write-Host($job.Name + " 已經完成")
Receive-Job $job
$taskCount--
Remove-Job $job
}
}
sleep 1
}
"所有任務已完成"
#得出任務運行的時間
(New-TimeSpan $start_time).totalseconds
以上的作用是等待所有后台任務結束后才會執行后續的操作,還有一種參數是-Any,它的作用是等待后台任務中任意一個介紹就會執行后續操作。
Remove-Job *
Start-Job -ScriptBlock{ sleep 2 } -Name a1
Start-Job -ScriptBlock { sleep 30} -Name a2
Wait-Job -Any *
等上述執行完成后,查看get-job,可以看到“sleep 30”還在進行。即有一個后台任務結束后就不在等待!
PS C:\Windows\system32> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
115 a1 BackgroundJob Completed False localhost sleep 2
117 a2 BackgroundJob Running True localhost sleep 30
6、remove-job
刪除后台運行的任務,前提條件是該后台任務是停止狀態
PS C:\> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
53 s1 BackgroundJob Completed False localhost sleep 5
55 s2 BackgroundJob Completed False localhost sleep 6
57 s3 BackgroundJob Completed False localhost sleep 7
59 rm BackgroundJob Completed False localhost sleep 20
PS C:\> Remove-Job -Name rm
PS C:\> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
53 s1 BackgroundJob Completed False localhost sleep 5
55 s2 BackgroundJob Completed False localhost sleep 6
57 s3 BackgroundJob Completed False localhost sleep 7
PS C:\> Remove-Job *
PS C:\> Get-Job
三、start-job傳入參數
將參數傳入start-job需要用到兩個,一個是花括號中的param(),另一個是ArguementList,具體實例如下。
PS C:\> $a = "name"
PS C:\> $b = "bill"
PS C:\> Start-Job -ScriptBlock{ param($a,$b) echo "$a == $b" } -ArgumentList $a,$b
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
67 Job67 BackgroundJob Running True localhost param($a,$b) echo "$a...
PS C:\> Receive-Job -id 67
name == bill
over!
