我有一系列正在运行的作业。完成后我使用receive-job并将输出写入屏幕。我想获取该输出并将其记录到文件中。我不希望在作业运行时产生输出,因为一次运行多个作业,记录将会散布。 Get-Job | 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和out-file仍然没有产生输出。
答案 0 :(得分:7)
注意:经过一些实验,我发现使用写输出或直接输出变量也不是一个好的解决方案。如果你有一个使用这些方法中的任何一个并且还返回一个值的函数,那么输出将与返回值连接!
我发现从作业记录输出的最佳方法是将write-host与Start-Transcript和Stop-Transcript cmdlet结合使用。有一些缺点,例如ISE不支持Transcript cmdlet。此外,我还没有找到一种方法输出到成绩单而不进入主机(我想要一个具有较少详细用户体验的健壮日志),但它似乎是目前可用的最佳解决方案。这是我能够记录并发作业而不必散布输出的唯一方法,而无需为每个作业更新多个临时日志文件,然后将它们组合在一起。
以下是我之前因为文档原因而离开的答案:
这里的问题是没有从Receive-Job获得输出。我希望看到的作业的输出是由写主机写入作业的。当调用receive-job时,我在屏幕上看到了我的所有输出,但没有其他东西可用于管道。似乎当我调用receive-job所有那些写主机cmdlet执行时,但是没有什么可以让我拿起并管道输出文件。以下脚本演示了这一点。您会注意到日志文件和主机中都出现“Sleepy Time”,而“Slept”仅出现在主机中。因此,我不需要尝试将Receive-Job传递给写主机,而是需要修改我的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
答案 1 :(得分:6)
如果作业使用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 :(得分:2)
我发现如何通过Receive-Job CMDlet接收Write-Host结果到变量,它正在使用Powershell 5.1:
$var = Receive-Job $job 6>&1
答案 3 :(得分:1)
当您收到作业结果时,会删除这些结果,以便进一步调用Receive-Job
将无法检索(假设作业已完成)。如果您不希望Receive-Job
删除结果,请使用-Keep
开关。现在,这并不能解释为什么第一次调用Receive-Job
时没有向日志文件输出任何内容...除非您没有显示正在执行Receive-Job
脚本的部分脚本在此之前。
答案 4 :(得分:1)
您可以将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中获取所有输出接收-工作
答案 5 :(得分:1)
我知道这很老了。我不经常使用作业,所以当我偶然发现此线程时,我显然遇到了同样的问题。无论如何,我得出了一个不同的解决方案,所以我想我应该把它扔出去。
在PowerShell 5.1中,我只是引用了Bob对象的属性,在我的情况下,它看起来像:
$Jobs.ChildJobs.Output | Out-File <LogFile> -Append
看起来所有流都以这种方式记录。简单的回声进入输出。写主机命令转到信息流。
Output : {}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
Information : {}
我希望无论如何对某人有帮助。
答案 6 :(得分:0)
解决问题的简单方法。 Write-Verbose&#34; blabla&#34; -verbose
$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
答案 7 :(得分:0)
我所做的是根本不调用 Receive-Job。但是创建一个包装脚本块来启动和停止脚本。然后在那个包装器脚本块中,我将作业脚本块称为
&$ScriptBlock <参数>
因此,脚本会将所有输出捕获到一个文件中。