web-dev-qa-db-ja.com

Powershell:Receive-Jobから出力を取得する

実行中のジョブのコレクションがあります。それらが完了すると、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)
}

編集:キースのコメントを見た後、私は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を使用していないことも確認しました。ただし、write-Host $ tempおよびout-fileは出力を生成しません。

20
Justin Holbrook

注:いくつかの実験の後、書き込み出力を使用するか、単に変数を直接出力することも、良い解決策ではないことがわかりました。これらのメソッドのいずれかを使用し、値を返す関数がある場合、出力は戻り値と連結されます!

ジョブからの出力をログに記録するために見つけた最善の方法は、write-HostをStart-TranscriptおよびStop-Transcriptコマンドレットと組み合わせて使用​​することです。 ISEがTranscriptコマンドレットをサポートしないなど、いくつかの欠点があります。また、ホストに行かずにトランスクリプトに出力する方法を見つけていませんが(冗長なユーザーエクスペリエンスのない堅牢なログが必要でした)、現在利用可能な最良のソリューションであるようです。これは、ジョブごとに複数の一時ログファイルを作成し、それらを結合することなく、出力を散在させずに同時ジョブを記録できる唯一の方法です。

以下は、文書化のためだけに残した前回の回答です。

ここでの問題は、Receive-Jobからの出力を得ていませんでした。期待していたジョブの出力は、write-Hostによってジョブに書き込まれています。 receive-jobが呼び出されたとき、画面にすべての出力が表示されましたが、他の場所にパイプを送ることができませんでした。それらのwrite-Hostコマンドレットがすべて実行するreceive-jobを呼び出すと、どうやら拾って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
7
Justin Holbrook

ジョブがWrite-Hostを使用して出力を生成する場合、Receive-Jobは$ nullを返しますが、結果はホストに書き込まれます。ただし、ジョブがWrite-Hostの代わりにWrite-Outputを使用して出力を生成する場合、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オブジェクトには、ジョブによって生成された文字列が含まれています。

7
doer

私は、Receive-Job CMDletで変数へのWrite-Hostの結果を受け取る方法を見つけました。Powershell5.1で動作しています。

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

ジョブの結果を受け取ると、それらの結果は削除され、Receive-Jobをさらに呼び出すと取得するものがなくなります(ジョブが完了したと仮定)。 Receive-Jobで結果を削除したくない場合は、-Keepスイッチを使用します。これは、Receive-Jobの最初の呼び出しがログファイルに何も出力しない理由を説明しません...表示していないスクリプトの一部がReceive-Jobこの前に。

1
Keith Hill

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 >>」受信ジョブ

1
T.CK

問題の簡単な解決策。 Write-Verbose "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
0
Dr Coyo