PowerShell脚本中Start-Job创建的作业无法通过Get-Job检测到
问题:PowerShell脚本启动的后台作业无法通过Get-Job检测到
我编写了一个PowerShell 7脚本,会根据当前时间启动sync-job.ps1作为后台作业并监控。脚本运行正常,数据传输符合预期,但脚本运行时执行Get-Job却找不到这些作业;直接在PowerShell环境中执行相同的Start-Job命令,却能正常创建并通过Get-Job检测到作业。
监控脚本
#-- Global Variables Write-Host "Enter Credentials for Network Share" $cred = Get-Credential $Source = Read-Host "Enter Source" $Destination = Read-Host "Enter Destination" $Sleepduration = Read-Host "Enter Sleepduration of Robocopy" $runtime_throttled_start = "0600" $runtime_throttled_end = "1759" $runtime_unthrottled_start = "1801" $runtime_unthrottled_end = "2359" $runtime_unthrottled_start_nextday = "0001" $runtime_unthrottled_end_nextday = "0558" $Date = Get-Date -Format "MM-dd-yyyy" $Logfile = "$PSScriptRoot\Logs\start-copy\start-copy-$Date.log" $Script_Root = "C:\temp" $Status = "0" #-- Functions function logging ($Value) { $Script_Output = "$(Get-Date -Format "MM-dd-yyyy HH:mm:ss") - $Value" Write-Host $Script_Output Write-Output $Script_Output >> $Logfile } function main { if (! (Test-Path "$PSScriptRoot\Logs\start-copy")) { Logging "Creating Logging folder..." New-Item -Path "$PSScriptRoot\Logs" -Name "start-copy" -Type "directory" } $i = 1 try { while ($i -eq 1) { $Time = get-date -Format "HHmm" if (($Time -ge $runtime_throttled_start) -and ($Time -le $runtime_throttled_end)) { if (($Status -eq "not_throttled") -or ($Status -eq 0)) { Logging "Stopping running jobs..." Get-Job | Where-Object {$_.Command -match "sync-job"} | Remove-Job -Force Logging "Starting throttled script..." Start-Job -Name Robocopy_Throttled -Filepath ".\sync-job.ps1" -ArgumentList "$Source", "$Destination", $Script_Root, $Sleepduration, "0", "$runtime_throttled_start-$runtime_throttled_end", "$cred" $nextday = $false } $Status = "throttled" Logging "Status: $Status" } if ((($Time -ge $runtime_unthrottled_start-1) -and ($Time -le ($runtime_unthrottled_end))) -or (($Time -ge $runtime_unthrottled_start_nextday) -and ($Time -le $runtime_unthrottled_end_nextday))) { if (($Status -eq "throttled") -or ($Status -eq 0)) { Logging "Stopping running jobs..." Get-Job | Where-Object {$_.Command -match "sync-job"} | Remove-Job -Force Logging "Starting unthrottled script..." Start-Job -Name Robocopy_Unthrottled -Filepath ".\sync-job.ps1" -ArgumentList "$Source", "$Destination", $Script_Root, $Sleepduration, "3", "$runtime_unthrottled_start-$runtime_unthrottled_end", "$cred" $nextday = $false } $Status = "not_throttled" Logging "Current status: $Status" } if (($Time -eq "0000") -and ($nextday -eq $false)) { $nextday = $true Logging "Next day, stopping running jobs once..." Get-Job | Where-Object {$_.Command -match "sync-job"} | Remove-Job -Force Logging "Starting next-day unthrottled script...." Start-Job -Name Robocopy_Unthrottled_nextday -Filepath ".\sync-job.ps1" -ArgumentList "$Source", "$Destination", $Script_Root, $Sleepduration, "3", "$runtime_unthrottled_start_nextday-$runtime_unthrottled_end_nextday", "$cred" $Status = "not_throttled_nextday" Logging "Current status: $Status" } Logging "Waiting 10 Seconds for the next check" Start-Sleep 10 Logging "The following Jobs are currently running: Get-Job | Where-Object {$_.Command -match "sync-job"} } } finally { Logging "Stopping and removing all jobs..." Get-Job | Where-Object {$_.Command -match "sync-job"} | Remove-Job -Force } } main
作业脚本(sync-job.ps1)
param ( $Source, $Destination, $Root, $Sleepduration, $Mode, $Runtime, $Cred ) #-- Global Variables $Date = Get-Date -Format "MM-dd-yyyy_HH-mm" $Time = Get-Date -Format "HH:mm" Write-Host $Logfile if ($Time -eq "00:00") { $Logfile = "$Root\Logs\Log-$Date-unthrottled_nextday.txt" } else { switch ($Mode) { 0 {$Logfile = "$Root\Logs\Log-$Date-throttled.txt"} 3 {$Logfile = "$Root\Logs\Log-$Date-unthrottled.txt"} } } #-- Functions function logging ($Value) { $Script_Output = "$(Get-Date -Format "MM-dd-yyyy HH:mm:ss") - $Value" Write-Host $Script_Output Write-Output $Script_Output >> $Logfile } function sync-folder ($Source, $Destination, $Mode) { if (!(Get-PSDrive -PSProvider FileSystem).Name -eq "TempMmount") { $cred = Get-Credential New-PSDrive -Name "TempMount" -PSProvider "FileSystem" -Credential $Cred -Root $Source } switch ($Mode) { 0 {robocopy $Source $Destination /e /z /copy:DAT /dcopy:DA /W:5 /xo /im /IPG:8 /log+:$Logfile /RH:$runtime /tee} 1 {robocopy $Source $Destination /e /z /copy:DAT /dcopy:DA /W:5 /xo /im /IPG:8 /log+:$Logfile /tee} 2 {robocopy $Source $Destination /e /z /copy:DAT /dcopy:DA /W:5 /xo /im /IPG:8 /log+:$Logfile /tee /L} 3 {robocopy $Source $Destination /e /z /copy:DAT /dcopy:DA /W:5 /xo /im /IPG:8 /log+:$Logfile /RH:$runtime /tee} Default {Write-Host "No Mode has been chosen. Exiting script..."; Exit} } Remove-PSDrive -Name "TempMount" } #-- Main Method function main { Write-Host "Source Directory: $Source" Write-Host "Destination Directory: $Destination" $Current_User = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name Logging "User who's executing this script: $Current_User" #-- Create Log Directory if it does not exist yet if (! (Test-Path "$Root\Logs")) { Logging "Creating Log Directory`n" New-Item -Path $Root -Name "Logs" -Type "Directory" } else { Logging "Log Directory already exists. Proceed.. `n" } $Modes = "(0) - Normal run (requires runtime)`n", "(1) - run only once`n", "(2) - Dry run once`n", "(3) - run without bandwidth limit (requires runtime)" Write-Host "`nYou got the following exectuion modes:`n`n $Modes`n" Write-Host "You have chosen the following Mode: $Mode" #-- Endless loop for repetetive sync #-- Sleepduration is set by the Sleepduration variable $i = 1 while ($i -eq 1) { Write-Host "===============================================================================`n" Write-Output "===============================================================================`n" >> $Logfile try { Logging "Starting Sync between $Source and $Destination `n" sync-folder $Source $Destination $Mode switch ($Mode) { 0 {Logging "Sync complete. Waiting $Sleepduration Seconds for the next Sync`n"; Start-Sleep $Sleepduration} 1 {Logging "Mode = run once, sync complete. Stoping script...`n"; Write-Host "`n===============================================================================`n"; Write-Output "`n===============================================================================`n" >> $Logfile; Exit} 2 {Logging "Mode = Dry run once, test complete. Stopping script..."; Write-Host "`n===============================================================================`n"; Write-Output "`n===============================================================================`n" >> $Logfile; Exit} 3 {Logging "Mode = no throttle, sync complete. Waiting $Sleepduration Seconds for the next Sync`n"; Start-Sleep $Sleepduration} } } catch { Logging "There was a Problem with the sync. Following Error message may be helpful:`n" Logging "$_`n" Write-Host "`n===============================================================================`n" Write-Output "`n===============================================================================`n" >> $Logfile } } } main
问题原因及修复方案
核心原因
脚本内启动的作业默认属于脚本本地作用域,而你在外部PowerShell会话(或当前会话的全局作用域)执行Get-Job时,只能读取当前作用域下的作业,因此无法看到脚本内创建的作业。
具体修复步骤
让作业全局可见(可选)
在启动作业时添加-Scope Global参数,这样外部会话就能通过Get-Job查到:Start-Job -Name Robocopy_Throttled -Scope Global -Filepath ".\sync-job.ps1" -ArgumentList "$Source", "$Destination", $Script_Root, $Sleepduration, "0", "$runtime_throttled_start-$runtime_throttled_end", $cred注意:全局作业容易和其他脚本的作业冲突,更推荐在脚本内部管理作业。
修复监控脚本的日志语法错误
原脚本中最后一段日志代码未闭合字符串,且未实际执行作业查询命令,修改为:$runningJobs = Get-Job | Where-Object {$_.Command -match "sync-job"} | Out-String Logging "The following Jobs are currently running:`n$runningJobs"修复作业脚本的驱动器判断逻辑
原sync-folder函数的驱动器判断逻辑错误,会重复弹出凭据窗口,修改为:function sync-folder ($Source, $Destination, $Mode) { # 检查TempMount驱动器是否存在,不存在则创建 if (-not (Get-PSDrive -Name TempMount -ErrorAction SilentlyContinue)) { New-PSDrive -Name "TempMount" -PSProvider "FileSystem" -Credential $Cred -Root $Source } # 保留原robocopy逻辑... }同时删除函数内的
$cred = Get-Credential,避免重复弹出凭据窗口。优化作业管理方式
建议在监控脚本中保存作业对象,避免靠命令匹配查找作业:# 全局变量保存当前作业 $currentJob = $null # 启动作业时保存对象 $currentJob = Start-Job -Name Robocopy_Throttled -Filepath ".\sync-job.ps1" -ArgumentList "$Source", "$Destination", $Script_Root, $Sleepduration, "0", "$runtime_throttled_start-$runtime_throttled_end", $cred # 停止作业时直接使用保存的对象 if ($currentJob) { Remove-Job $currentJob -Force $currentJob = $null }
内容的提问来源于stack exchange,提问作者Katzsuya
相关产品推荐
相关产品推荐

