为什么 Write-Host 在 powershell 作业中运行时不起作用?

Bin*_*man 6 powershell jobs write-host

抱歉,如果我是一个愚蠢的 powershell 菜鸟,但是作业显然无法写入终端有什么问题吗?我该如何解决这个问题?

# test.ps1
function myjob {
    Write-Host "Hello, World!" # doesn't show
}
Start-Job -Name MyJob -ScriptBlock ${function:myjob}
Wait-Job MyJob
Remove-Job MyJob
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 6

听起来您正在尝试Write-Host从后台作业直接同步写入控制台(终端)。

但是,PowerShell作业不允许直接访问调用者的控制台任何输出 - 甚至到 PowerShell主机(如果在其中运行,则在前台使用的是控制台)都通过 PowerShell 的输出流系统进行路由(请参阅概念性about_Redirection帮助主题)。

因此,始终需要Receive-Jobcmdlet 才能接收PowerShell 作业的输出。

以下示例同步接收作业输出,即阻塞执行,直到作业完成 ( -Wait),然后将其删除 ( -AutoRemoveJob);请参阅底部部分了解异步(轮询、非阻塞)方法。

$null = Start-Job -Name MyJob -ScriptBlock { Write-Host "Hello, World!" } 
Receive-Job -Wait -AutoRemoveJob -Name  MyJob
Run Code Online (Sandbox Code Playgroud)

Write-Host警告在工作中重新使用

  • 前台使用中,Write-Host输出 - 即使主要设计为发送到主机(控制台) - 也可以通过信息流(其编号为6,在 PSv5+ 中可用)进行重定向或捕获;例如:

    # OK - no output
    Write-Host 'silence me' 6>$null
    
    Run Code Online (Sandbox Code Playgroud)
  • Write-Host但是,从 PowerShell 7.2.1 开始,无法重定向或捕获通过(基于子进程的)后台作业接收的输出:

    # !! `silence me` still prints. 
    Start-Job { Write-Host 'silence me' } | Receive-Job -Wait -AutoRemoveJob 6>$null
    
    Run Code Online (Sandbox Code Playgroud)
    • 相比之下,当使用(通常更可取的)基于线程的后台作业(而不是基于子进程的后台作业)时,可以通过以下方式重定向/捕获它:Start-ThreadJob

      # OK - no output
      Start-ThreadJob { Write-Host 'silence me' } | Receive-Job -Wait -AutoRemoveJob 6>$null
      
      Run Code Online (Sandbox Code Playgroud)

等待作业以非阻塞方式完成,并在作业输出可用时传递它

# Start a simple job that writes a "." to the host once a second,
# for 5 seconds
$job = Start-Job $job -ScriptBlock { 
         1..5| ForEach-Object { Write-Host -NoNewLine .; Start-Sleep 1 } 
       } 

"Waiting for job $($job.Id) to terminate while passing its output through..."

do {
  $job | Receive-Job  # See if job output is available (non-blocking) and pass it through
  Start-Sleep 1       # Do other things or sleep a little.
} while (($job | Get-Job).State -in 'NotStarted', 'Running')

"`nJob terminated with state '$($job.State)'."

$job | Remove-Job     # Clean up.
Run Code Online (Sandbox Code Playgroud)
  • 注意:在这个简单的情况下,预期的终止状态是Completed(没有或仅发生终止错误)或Failed(生成了脚本终止错误throw(并且未在作业中捕获))。

  • 返回的作业对象Start-Job(而不是通过参数自行选择的名称-Name)用于与作业交互。这消除了给定 可能存在多个作业的歧义-Name,所有这些作业都将成为目标。

  • 谢谢大佬,你太棒了!我想从现在起我可以处理剩下的事情了:) (2认同)