25

実行中のジョブのコレクションがあります。完了したら receive-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 はまだ出力を生成しません。

4

8 に答える 8

12

ジョブが 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 オブジェクトには、ジョブによって生成された文字列が含まれます。

于 2015-01-28T19:41:22.883 に答える
7

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

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

以下は、ドキュメント上の理由で残した以前の回答です。

ここでの問題は、Receive-Jobから出力を取得していませんでした。私が期待していたジョブからの出力は、write-hostによってジョブに書き込まれています。receive-jobが呼び出されたとき、画面にすべての出力が表示されましたが、他の場所にパイプするために利用できるものはありませんでした。receive-jobを呼び出すと、これらすべてのwrite-hostコマンドレットが実行されるように見えますが、ピックアップして出力ファイルにパイプするものは何もありません。次のスクリプトはこれを示しています。「SleepyTime」はログファイルとホストの両方に表示されますが、「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 に答える
1

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

于 2012-08-15T17:17:40.263 に答える
0

問題の簡単な解決策。Write-Verbose "blabla" -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
于 2016-11-09T14:18:14.830 に答える
0

私がやったことは、Receive-Job をまったく呼び出さないことです。ただし、トランスクリプトを開始および停止するラッパー スクリプトブロックを作成します。次に、そのラッパー スクリプト ブロックで、ジョブ スクリプト ブロックを次のように呼び出します。

&$ScriptBlock < パラメータ >

したがって、トランスクリプトはすべての出力をファイルにキャプチャします。

于 2021-06-19T21:52:08.587 に答える