Powershell:从 Receive-Job 获取输出
Posted
技术标签:
【中文标题】Powershell:从 Receive-Job 获取输出【英文标题】:Powershell: get output from Receive-Job 【发布时间】:2012-08-12 00:17:40 【问题描述】:我有一组正在运行的作业。当他们完成后,我使用接收作业并将输出写入屏幕。我想获取该输出并将其记录到文件中。我不想在作业运行时输出产生的输出,因为一次运行多个作业时会穿插日志记录。找工作 | Receive-Job 以井井有条的方式打印输出。
我已经尝试了以下所有方法,但没有将输出写入文件或存储在变量中,它只是进入屏幕:
#Wait for last job to complete
While (Get-Job -State "Running")
Log "Running..." -v $info
Start-Sleep 10
Log ("Jobs Completed. Output:") -v $info
# Getting the information back from the jobs
foreach($job in Get-Job)
Receive-Job -Job $job | Out-File c:\Test.log -Append
$output = Receive-Job -Job $job
Log ("OUTPUT: "+$output)
Receive-Job -Job $job -OutVariable $foo
Log ("FOO: "+$foo)
编辑: 在看到 Keith 的评论后,我已将 foreach 中额外的 Receive-Job 调用删除到以下内容:
# Getting the information back from the jobs
foreach($job in Get-Job)
Receive-Job -Job $job -OutVariable temp
Write-Host ("Temp: "+$temp)
$temp | Out-File -FilePath c:\Test.log -Append
我还确认我没有在脚本的其他任何地方使用 Receive-Job。但是写入主机 $temp 和输出文件仍然不产生任何输出。
【问题讨论】:
这是一个很好的问题。正如您在下面提到的,Write-Output 没有帮助,因为它会干扰函数调用返回的内容。我认为 Write-Verbose 可能会很有帮助,因为您可以在作业的子作业的 Verbose 字段上执行 ReadAll,但只有在设置了 VerbosePreference 时才会填充它,这意味着它也会显示在输出中。我真的认为我们需要修复这个错误:github.com/PowerShell/PowerShell/issues/3130 见@Spongebob 下面的代码答案。它使您可以轻松地将子作业的 Write-Host 的输出转换为变量,这很棒。我在工作中将此与 Start-Transcript/Stop-Transcript 结合使用。然后我得到了每个作业的日志,但是每个作业的日志都可以通过每个 Receive-Job 进行检查。仍然存在您的***控制台被子作业的 Write-Host 调用污染的问题,但我在这里看到的没有解决方案可以解决这个问题。 【参考方案1】:如果作业使用 Write-Host 生成输出,Receive-Job 返回 $null,但结果会写入主机。但是,如果作业使用 Write-Output 代替 Write-Host 生成输出,则 Receive-Job 返回作业输出的字符串数组 [string[]]。
为了演示,在 PowerShell 提示符处输入这个无害的代码:
$job = Start-Job -ScriptBlock
[int] $counter = 0
while ($counter -lt 10)
Write-Output "Counter = $counter."
Start-Sleep -Seconds 5
$counter++
等待大约 20-30 秒,让作业产生一些输出,然后输入以下代码:
$result = Receive-Job -Job $job
$result.Count
$result
$result | Get-Member
$result 对象包含作业生成的字符串。
【讨论】:
【参考方案2】:注意:经过一些实验,我发现使用写输出或直接输出变量也不是一个好的解决方案。如果您有一个函数使用这些方法中的任何一个并且还返回一个值,则输出将与返回值连接!
我发现记录作业输出的最佳方法是将 write-host 与 Start-Transcript 和 Stop-Transcript cmdlet 结合使用。有一些缺点,例如 ISE 不支持 Transcript cmdlet。此外,我还没有找到一种在不发送到主机的情况下输出到脚本的方法(我想要一个具有较少冗长用户体验的健壮日志),但它似乎是目前可用的最佳解决方案。这是我能够记录并发作业的唯一方法,而无需分散输出,而无需为每个作业纠正多个临时日志文件,然后将它们组合起来。
以下是我之前出于文档原因留下的答案:
这里的问题是没有从 Receive-Job 获得输出。我期望看到的作业的输出正在由 write-host 写入作业。当调用接收作业时,我在屏幕上看到了我的所有输出,但在其他地方没有可用管道。似乎当我调用接收作业时,所有这些写入主机 cmdlet 都会执行,但是我没有什么可以获取并通过管道传输到输出文件。以下脚本演示了这一点。您会注意到“Sleepy Time”同时出现在日志文件和主机中,而“Slept”仅出现在主机中。因此,与其尝试将 Receive-Job 传送到 write-host,我需要修改我的 Log 函数以不使用 write-host 或拆分输出,如下所示。
cls
rm c:\jobs.log
$foo = @(1,2,3)
$jobs = @()
foreach($num in $foo)
$s = get-process -name "explorer"
Start-Sleep $args[0]
$x = ("Sleepy Time: "+$args[0])
write-host $x
$x
write-host ("Slept: "+$args[0])
$id = [System.Guid]::NewGuid()
$jobs += $id
Start-Job -Name $id -ScriptBlock $s -args $num
While (Get-Job -State "Running")
cls
Get-Job
Start-Sleep 1
cls
Get-Job
write-host "Jobs completed, getting output"
foreach($job in $jobs)
Receive-Job -Name $job | out-file c:/jobs.log -append
Remove-Job *
notepad c:\jobs.log
下面是一个示例,它将运行并发作业,然后按照 mbourgon 的请求顺序记录它们。您会注意到时间戳表明工作是散布的,但日志是按作业顺序显示的。
#This is an example of logging concurrent jobs sequentially
#It must be run from the powershell prompt as ISE does not support transcripts
cls
$logPath = "c:\jobs.log"
rm $logPath
Start-Transcript -Path $logPath -Append
#Define some stuff
$foo = @(0,1,2)
$bar = @(@(1,"AAA"),@(2,"BBB"),@(3,"CCC"))
$func =
function DoWork1
Log "This is DoWork1"
return 0
function DoWork2
Log "This is DoWork2"
return 0
function Log
Param([parameter(ValueFromPipeline=$true)]$InputObject)
$nl = [Environment]::NewLine.Chars(0)
$time = Get-Date -Format HH:mm:ss
Write-Host ($time+">"+$InputObject+$nl)
#Start here
foreach($num in $foo)
$s = $num = $args[0]
$bar = $args[1]
$logPath = $args[2]
Log ("Num: "+$num)
for($i=0; $i -lt 5; $i++)
$out = ([string]::$i+$bar[$num][1])
Log $out
start-sleep 1
Start-Sleep $num
$x = DoWork1
Log ("The value of x is: "+$x)
$y = DoWork2
Log ("The value of y is: "+$y)
Start-Job -InitializationScript $func -ScriptBlock $s -args $num, $bar, $logPath
While (Get-Job -State "Running")
cls
Get-Job
Start-Sleep 1
cls
Get-Job
write-host "Jobs completed, getting output"
Get-Job | Receive-Job
Remove-Job *
Stop-Transcript
notepad $logPath
【讨论】:
有什么办法可以得到您提到的“当前最佳方式”的副本?谢谢;我也遇到了同样的问题。 我在答案末尾添加了一个代码示例,希望这很有用。【参考方案3】:我找到了如何使用 Receive-Job CMDlet 将 Write-Host 结果接收到变量的解决方案,它适用于 Powershell 5.1:
$var = Receive-Job $job 6>&1
【讨论】:
如果有人好奇它是如何工作的:docs.microsoft.com/en-us/powershell/module/… 来自 msdn:>&1 Redirects the specified stream to the Success stream.
【参考方案4】:
我知道这是旧的。我不经常使用工作,所以当我偶然发现这个线程时,我显然遇到了同样的问题。无论如何,我找到了一个不同的解决方案,所以我想我会把它扔在那里。
同样在 PowerShell 5.1 中,我只是简单地引用了 Job Object(s) 上的属性在我的例子中,这看起来像:
$Jobs.ChildJobs.Output | Out-File <LogFile> -Append
看起来所有的流都是以这种方式记录的。简单的回显到输出,推测是成功流。 Write-Host
命令进入信息流。
Output :
Error :
Progress :
Verbose :
Debug :
Warning :
Information :
【讨论】:
【参考方案5】:当您收到作业的结果时,这些结果将被删除,这样对Receive-Job
的进一步调用将无法检索(假设作业已完成)。如果您不想Receive-Job
删除结果,请使用-Keep
开关。现在这并不能解释为什么您第一次调用Receive-Job
没有向日志文件输出任何内容......除非您没有显示的脚本的一部分在此之前正在执行Receive-Job
。
【讨论】:
Keith 我修改了我的脚本,所以接收作业只被调用一次,并且验证的接收作业不在脚本中的其他任何地方使用。 顺便说一句,当您使用-OutVariable
时,请为其提供变量名而不是变量,例如-ov temp
。然后您稍后使用$temp
访问该信息。
是的,抱歉打错了……我已经对这件事做了很多改动。【参考方案6】:
您可以通过管道将您的 Get-Process 传输到 Format-Table,这样您就可以看到所有的输出。
Get-Process -Name "explorer" | format-Table -hidetableheaders -AutoSize
您可以在脚本末尾使用类似这样的内容...
Get-job | Receive-Job 2>&1 >> c:\path\to\your\log\file.log
"2>&1 >>" 获取 Get-Job 的所有输出 |接收作业
【讨论】:
【参考方案7】:问题的简单解决方案。写详细的“blabla”-详细
$s = New-PSSession -ComputerName "Computer"
$j = Invoke-Command -Session $s -ScriptBlock Write-Verbose "Message" -Verbose -AsJob
Wait-Job $j
$Child = $j.ChildJobs[0]
$Child.Verbose | out-file d:/job.log -append
【讨论】:
【参考方案8】:我所做的是根本不调用 Receive-Job。但是创建一个包装脚本块来启动和停止转录。然后在该包装脚本块中,我将作业脚本块称为
&$ScriptBlock
所以脚本会将所有输出捕获到一个文件中。
【讨论】:
以上是关于Powershell:从 Receive-Job 获取输出的主要内容,如果未能解决你的问题,请参考以下文章
powershell 使用powershell #powershell从zip文件中删除文件
如何从其他powershell脚本调用powershell脚本并且脚本是在powershell对象而不是文件中分配的