Powershell:从接收作业获取输出

egmofgnx  于 2023-02-04  发布在  Shell
关注(0)|答案(8)|浏览(161)

我有一个正在运行的作业集合。当它们完成时,我使用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)
}

编辑:在看到基思的评论后,我已经删除了前面额外的接收工作调用:

# 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,但是write-host $temp和out-file仍然没有输出。

jei2mxaa

jei2mxaa1#

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

pepwfjgg

pepwfjgg2#

注意:经过一些实验,我发现使用写输出或直接输出变量也不是一个好的解决方案。如果你有一个函数使用这两种方法中的任何一种,并且也返回一个值,那么输出将与返回值连接起来!
我发现记录作业输出的最佳方法是将write-host与Start-Transcript和Stop-Transcript cmdlet结合使用。但也有一些缺点,例如伊势不支持Transcript cmdlet。此外,我还没有找到一种方法,可以在不将记录输出到主机的情况下将其输出到记录(我想要一个健壮的日志,用户体验不那么冗长),但这似乎是目前最好的解决方案,这是我唯一的方法我已经能够记录并发作业,而不必分散输出,不必为每个作业修改多个临时日志文件,然后将它们组合起来。
以下是我之前的回答,我只是出于文档原因而留下:
此处的问题是未从Receive-Job获取输出。我期望看到的作业输出正在由write-host写入作业中。调用receive-job时,我在屏幕上看到了所有输出,但没有任何输出可用于通过管道传输到其他位置。似乎当我调用receive-job时,所有这些write-host cmdlet都会执行,但是我没有任何东西可以拾取并通过管道发送到out-file。下面的脚本演示了这一点。您会注意到“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
u3r8eeie

u3r8eeie3#

我找到了如何使用Receive-Job CMDlet接收Write-Host结果到变量的解决方案,它在Powershell 5.1上工作:

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

m4pnthwp4#

我知道这是旧的。我不经常使用作业,所以当我偶然发现这个线程时,我显然有同样的问题。无论如何,我得到了一个不同的解决方案,所以我想我应该把它扔在那里。
同样在PowerShell 5.1中,我只是引用了作业对象的属性。在我的示例中,如下所示:

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

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

Output        : {}
Error         : {}
Progress      : {}
Verbose       : {}
Debug         : {}
Warning       : {}
Information   : {}
a0zr77ik

a0zr77ik5#

当您收到作业的结果时,这些结果将被删除,这样对Receive-Job的进一步调用将没有任何可检索的内容如果你不想让Receive-Job删除结果,使用-Keep开关,这并不能解释为什么你第一次调用Receive-Job没有输出任何东西到日志文件...除非在此之前有一部分脚本没有显示,它正在执行Receive-Job
顺便说一句,当你使用-OutVariable时,给它提供一个变量名,而不是一个变量,例如-ov temp。然后你以后用$temp访问这个信息。

pkln4tw6

pkln4tw66#

您可以通过管道将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

从Get-Job获取所有输出的“2〉&1〉〉”|接收作业

w8ntj3qf

w8ntj3qf7#

一个简单的解决问题的方法。Write-冗长“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
8ehkhllq

8ehkhllq8#

我所做的是根本不调用Receive-Job,而是创建一个用于启动和停止脚本的 Package 器脚本块,然后在该 Package 器脚本块中调用jobs脚本块,如下所示
&$脚本块< Parameters >
因此,脚本将捕获所有输出到一个文件中。

相关问题