25

我有一组正在运行的作业。当他们完成后,我使用接收作业并将输出写入屏幕。我想获取该输出并将其记录到文件中。我不想在作业运行时输出产生的输出,因为一次运行多个作业时会穿插日志记录。找工作 | 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)
}

编辑:在看到基思的评论后,我已将 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 和输出文件仍然不产生任何输出。

4

8 回答 8

12

如果作业使用 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 对象包含作业生成的字符串。

于 2015-01-28T19:41:22.883 回答
7

注意:经过一些实验,我发现使用 write-output 或直接输出变量也不是一个好的解决方案。如果您有一个函数使用这些方法中的任何一个并且还返回一个值,则输出将与返回值连接!

我发现记录作业输出的最佳方法是将 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
于 2012-08-16T16:41:59.290 回答
7

我发现了如何使用 Receive-Job CMDlet 将 Write-Host 结果接收到变量的解决方案,它适用于 Powershell 5.1:

$var = Receive-Job $job 6>&1
于 2017-02-16T05:30:43.313 回答
3

我知道这是旧的。我不经常使用工作,所以当我偶然发现这个线程时,我显然遇到了同样的问题。无论如何,我得出了一个不同的解决方案,所以我想我会把它扔在那里。

同样在 PowerShell 5.1 中,我只是引用了 Job Object(s) 上的属性在我的情况下,这看起来像:

$Jobs.ChildJobs.Output | Out-File <LogFile> -Append

看起来所有的流都是以这种方式记录的。简单的回显到输出,推测是成功流。 Write-Host命令转到信息流。

Output        : {}
Error         : {}
Progress      : {}
Verbose       : {}
Debug         : {}
Warning       : {}
Information   : {}
于 2020-02-12T01:57:42.547 回答
1

当您收到作业的结果时,这些结果将被删除,这样进一步的调用Receive-Job将无法检索(假设作业已完成)。如果您不想Receive-Job删除结果,请使用-Keep开关。现在这并不能解释为什么您的第一次调用Receive-Job没有向日志文件输出任何内容......除非您没有显示的脚本的一部分Receive-Job在此之前正在执行。

于 2012-08-15T17:17:40.263 回答
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 | 获取所有输出 接收工作

于 2013-12-20T01:40:55.720 回答
0

一个简单的解决问题的方法。写详细的“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
于 2016-11-09T14:18:14.830 回答
0

What I have done is to not call Receive-Job at all. But create a wrapper scriptblock that starts and stops a transcripts. Then in that wrapper script block I call the jobs script block like

&$ScriptBlock < Parameters >

So the transcript will capture all the output to a file.

于 2021-06-19T21:52:08.587 回答