Powershell: получать результаты от приема-работы

13 Justin Holbrook [2012-08-15 20:06:00]

У меня есть набор заданий, которые выполняются. Когда они заканчиваются, я использую прием-работу и записываю вывод на экран. Я хотел бы взять этот вывод и записать его в файл. Я не хочу получать результаты, созданные во время выполнения заданий, потому что с несколькими заданиями, запущенными сразу, запись будет чередоваться. 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)
}

EDIT: Я удалил дополнительные вызовы Receive-Job в foreach следующим образом, увидев комментарий Keith:

# 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 где-нибудь еще в script. Записи $temp write-host и out-file все равно не выдают результат.

powershell jobs


6 ответов


6 Решение Justin Holbrook [2012-08-16 19:41:00]

ПРИМЕЧАНИЕ. После некоторых экспериментов я обнаружил, что использование write-output или просто вывод переменной напрямую не является хорошим решением. Если у вас есть функция, которая использует любой из этих методов, а также возвращает значение, результат будет конкатенирован с возвращаемым значением!

Лучший способ найти выход из заданий - использовать write-host в сочетании с командлетами Start-Transcript и Stop-Transcript. Есть некоторые недостатки, такие как ISE не поддерживает командлеты Transcript. Кроме того, я не нашел способ вывода в расшифровку без его хостинга (мне нужен надежный журнал с менее подробным пользовательским интерфейсом), но, похоже, это лучшее решение, доступное в настоящее время. Это единственный способ, с помощью которого я мог регистрировать параллельные задания без вставки вывода без необходимости перебора нескольких временных файлов журнала для каждого задания, а затем их объединения.

Ниже был мой предыдущий ответ, который я оставил только по причинам документации:

Проблема здесь не была получена из Receive-Job. Вывод из заданий, которые я ожидал увидеть, записывается в задание с помощью write-host. Когда была получена задание на получение, я видел весь свой вывод на экране, но ничего не было доступно для прокладки в другом месте. Кажется, когда я вызываю get-job, все эти командлеты write-host выполняются, но мне нет ничего, что можно было бы забрать и вывести из файла. Это демонстрирует следующий script. Вы заметите, что "Сонное время" появляется как в файле журнала, так и на хосте, а "Спящий" появляется только на хосте. Поэтому вместо того, чтобы пытаться подключить 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

4 doer [2015-01-28 22:41:00]

Если задание использует 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 содержит строки, созданные заданием.


1 T.CK [2013-12-20 04:40:00]

Вы можете подключить Get-Process к Format-Table, чтобы вы могли видеть весь вывод.

    Get-Process -Name "explorer" | format-Table -hidetableheaders -AutoSize

И вы можете использовать что-то вроде этого в конце script...

    Get-job | Receive-Job 2>&1 >> c:\path\to\your\log\file.log

"2 > & 1 → " с захватом всего вывода из Get-Job | Receive-Job


1 Keith Hill [2012-08-15 20:17:00]

Когда вы получаете результаты задания, эти результаты удаляются так, что дальнейшие вызовы Receive-Job не смогут ничего извлечь (при условии, что задание выполнено). Если вы не хотите, чтобы Receive-Job удалял результаты, используйте переключатель -Keep. Теперь это не объясняет, почему ваш первый вызов Receive-Job не выводит ничего в файл журнала... если только часть script вы не показываете, что делает Receive-Job до этого.


1 SpongeBob the codder [2017-02-16 08:30:00]

Я понял, как получать результаты Write-Host в переменную с CMDlet Receive-Job, он работает над Powershell 5.1:

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

0 Dr Coyo [2016-11-09 17:18:00]

Простое решение проблемы. Запись-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