Powershell:从接收作业获取输出

Powershell:从接收作业获取输出,powershell,jobs,Powershell,Jobs,我有一个正在运行的作业集合。当他们完成时,我使用接收作业,它将输出写入屏幕。我想获取该输出并将其记录到一个文件中。我不想在作业运行时生成输出,因为如果同时运行多个作业,日志记录将被分散。Get Job | Receive Job以良好的组织方式打印输出 我尝试了以下所有操作,但没有将任何输出写入文件或存储在变量中,它只会显示在屏幕上: #Wait for last job to complete While (Get-Job -State "Running") { Log "Ru

我有一个正在运行的作业集合。当他们完成时,我使用接收作业,它将输出写入屏幕。我想获取该输出并将其记录到一个文件中。我不想在作业运行时生成输出,因为如果同时运行多个作业,日志记录将被分散。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中的额外接收工作电话删除为以下内容:

# 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和输出文件仍然不会产生任何输出。

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

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

我发现记录作业输出的最佳方法是将write host与Start-Transcript和Stop-Transcript cmdlet结合使用。存在一些缺点,例如ISE不支持转录cmdlet。此外,我还没有找到一种不将其发送到主机就输出到转录本的方法(我想要一个具有较少详细用户体验的健壮日志),但它似乎是目前可用的最佳解决方案。这是我能够记录并发作业的唯一方法,而无需为每个作业设置多个临时日志文件的权限,然后将它们组合在一起,而无需插入输出

以下是我之前的回答,我离开只是出于文档原因:

这里的问题是无法从接收作业获取输出。我希望看到的作业的输出正在由写入主机写入作业中。当receive job被调用时,我在屏幕上看到了所有的输出,但没有任何东西可用于其他管道。当我调用receive job时,似乎所有的write-host cmdlet都会执行,但我并没有任何东西可以拾取并导出文件。下面的脚本演示了这一点。您会注意到“睡眠时间”同时出现在日志文件和主机中,而“睡眠时间”只出现在主机中。因此,我需要修改日志函数,使其不使用write host或拆分输出,而不是尝试通过管道将接收作业传递给write host,如下所示

下面是一个示例,它将运行并发作业,然后根据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

您可以通过管道将Get进程设置为表格格式,以便查看所有输出

    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 | Receive Job获取所有输出

如果作业使用写入主机生成输出,则Receive Job返回$null,但结果会写入主机。但是,如果作业使用写输出代替写主机生成输出,则接收作业将返回作业输出的字符串数组[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对象包含作业生成的字符串。

此问题的简单解决方案。写冗长的“废话”-冗长的



我使用receive Job CMDlet找到了如何接收将主机结果写入变量的解决方案,它在Powershell 5.1上运行:

$var = Receive-Job $job 6>&1

我知道这是旧的。我不经常使用作业,所以当我偶然发现这条线索时,我显然也遇到了同样的问题。无论如何,我找到了一个不同的解决方案,所以我想我应该把它扔出去

同样在PowerShell 5.1中,我只是引用了作业对象上的属性,在我的示例中,如下所示:

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

Keith I修改了我的脚本,因此只调用了一次receive job,并且验证了receive job不会在脚本中的任何其他位置使用。顺便说一句,当您使用
-OutVariable
时,请提供变量名而不是变量,例如
-ov temp
。然后你以后用
$temp
访问该信息。是的,很抱歉这是一个打字错误。。。我已经改变了很多。有没有办法得到你提到的“当前最佳方式”的副本?谢谢我也有同样的问题。我在答案的末尾添加了一个代码示例,希望这是有用的。这是一个很棒的问题。正如您在下面提到的,写输出没有帮助,因为它会干扰函数调用返回的内容。我认为Write Verbose可能会很有帮助,就像您可以在作业的子作业的Verbose字段上执行ReadAll一样,但只有在设置了Verbose首选项时才会填充它,这意味着它也会显示在输出中。真的,我认为我们需要修复这个bug:请参见下面的@Spongebob密码器答案。它可以让您轻松地将子作业的写入主机的输出转换为变量,这非常好。在我的工作中,我将其与开始记录/停止记录结合使用。然后我有每个作业的日志,但是每个作业的日志都可以与每个接收作业一起检查。您的顶级控制台仍然存在被子作业的写主机调用污染的问题,但我在这里看到的任何解决方案都无法解决这个问题。如果有人好奇它是如何工作的:从msdn:
&1重定向规范
$var = Receive-Job $job 6>&1
$Jobs.ChildJobs.Output | Out-File <LogFile> -Append
Output        : {}
Error         : {}
Progress      : {}
Verbose       : {}
Debug         : {}
Warning       : {}
Information   : {}