Powershell: захват удаленных потоков вывода в комбинации Invoke-Command + Invoke-Expression - PullRequest
1 голос
/ 06 марта 2020

Поскольку я не нашел решения, выполнив поиск по форуму, и потратил некоторое время на то, чтобы выяснить, как это сделать правильно, я помещаю здесь вопрос вместе с рабочим решением.

Сценарий: в Powershell, необходимо удаленно выполнить блок скрипта, хранящийся в переменной, и записать его вывод для дальнейшей обработки. Вывод не должен отображаться на экране, если скрипт не генерирует его специально. Блок сценария может содержать команды Write-Warning.

Ответы [ 2 ]

0 голосов
/ 06 марта 2020

Обратите внимание, что интересующее поведение применяется , как правило, к командам PowerShell , а не только в контексте Invoke-Command и - , как правило, которых следует избегать - Invoke-Expression; в вашем случае нужно только обойти ошибку . [1]


Ваш собственный ответ показывает, как перенаправить один специфицирует c выходных потоков в поток вывода успеха ; Например, 3>&1 перенаправляет (>&) поток предупреждения (3) в поток успеха (вывода) (1).

& указывает, что целью перенаправления является поток , в отличие от файла ; для получения дополнительной информации о выходном потоке PowerShell см. about_Redirection.

Если вы хотите перенаправить все выходных потоков в поток вывода успеха используйте перенаправление *>&1

Путем перенаправления всех потоков в выходной поток их объединенный вывод может быть записан в переменную, перенаправлен в файл или отправлен через конвейер , тогда как по умолчанию захватывается только выходной поток success (1).

Отдельно , вы можете использовать общие параметры именованные -*Variable параметры для захвата выходных данных отдельного потока в переменных для некоторых потоков, а именно:

  • Поток 1 (успех): -OutVariable
  • Поток 2 (ошибка): -ErrorVariable
  • Поток 3 (предупреждение): -WarningVariable
  • Поток 6 (информация): -InformationVariable

Обязательно указывайте целевую переменную только имя , без $ префикс ; например, для захвата предупреждений в переменной $warnings используйте
-WarningVariable warnings, например, в следующем примере:
Write-Warning hi -WarningVariable warnings; "warnings: $warnings"

Обратите внимание, что с -*Variable выходные данные потока собираются в переменную независимо от того, отключите ли вы этот поток или даже игнорируете его в противном случае , с заметным исключением 1113 *, в этом случае -ErrorVariable переменная не заполнена (и ошибка также не записана в automati c $Error переменная , которая в противном случае записывает все ошибки, которые происходят в сессия).

Как правило, -{StreamName}Action SilentlyIgnore представляется эквивалентным {StreamNumber}>$null.


Обратите внимание на отсутствие многословия (4) и потоки отладки (5) выше; вы можете только захватить их косвенно , через 4>&1 и 5>&1 (или *>&1), что затем требует от вас извлечения интересующего результата из объединенного поток через фильтрацию по выходному объекту тип :

Важно :

  • Подробное (4) и потоки отладки (5) - это единственные два потока, тихие в источнике по умолчанию ; то есть , если только эти потоки явно не включены через -Verbose / -Debug или их переменную предпочтения эквивалентов, $VerbosePreference = 'Continue' / $DebugPreference = 'Continue', ничего не является испускается и ничего не может быть захвачено .

  • Информационный поток (5) равен только без звука на выходе по умолчанию ; то есть запись в информационный поток (с Write-Information) всегда записывает объекты в поток, но они не отображаются по умолчанию (они отображаются только с -InformationAction Continue / $InformationPreference = 'Continue')

    • Начиная с версии 5, Write-Host теперь тоже записывает в информационный поток, хотя его вывод выводит по умолчанию, но можно подавить с помощью 6>$null или -InformationAction Ignore (но не -InformationAction SilentlyContinue).
# Sample function that produces success and verbose output.
# Note that -Verbose is required for the message to actually be emitted.
function foo { Write-Output 1; Write-Verbose -Verbose 4 }

# Get combined output, via 4>&1
$combinedOut = foo 4>&1

# Extract the verbose-stream output records (objects).
# For the debug output stream (5), the object type is
# [System.Management.Automation.DebugRecord]
$verboseOut = $combinedOut.Where({ $_ -is [System.Management.Automation.VerboseRecord] })

[1] Ошибка захвата потока, начиная с PowerShell v7.0:

В двух словах: В контексте удаленное взаимодействие (например, Invoke-Command -Session здесь), фоновые задания и так называемые миниоболочки (передача блока сценария в CLI PowerShell для выполнения команд в дочернем процессе), только ожидаемые (1) и ошибочные (2) потоки могут быть перехвачены, как и ожидалось ; все остальные неожиданно передаются на хост (дисплей) - см. эту проблему GitHub .

Ваша команда должна - но в настоящее время не работает - работает следующим образом, что избавило бы от необходимости Invoke-Expression:

# !! 3>&1 redirection is BROKEN as of PowerShell 7.0, if *remoting* is involved
# !! (parameters -Session or -ComputerName).
$RemoteOutput = 
  Invoke-Command -Session $Session $Commands 3>&1 -ErrorVariable RemoteError 2>$null

То есть, в принципе, вы должны иметь возможность передавать переменную $Commands, которая содержит блок скрипта, непосредственно как (подразумеваемый) аргумент -ScriptBlock до Invoke-Command.

0 голосов
/ 06 марта 2020

Блок скрипта содержится в переменной $Commands. $Session - это уже установленный сеанс удаленного взаимодействия Powershell.

Задача решается с помощью следующей команды:

$RemoteOutput = 
  Invoke-Command -Session $Session {
    Invoke-Expression $Using:Commands 3>&1
  } -ErrorVariable RemoteError 2>$null

После выполнения команды весь вывод блока сценария содержится в $RemoteOutput. Ошибки, сгенерированные во время удаленного выполнения кода, помещаются в $RemoteError.

Дополнительные пояснения. Write-Warning в Invoke-Expression кодовом блоке генерирует свой собственный выходной поток, который не перехватывается Invoke-Command. Единственный способ записать его в переменную - перенаправить этот поток в стандартный поток Invoke-Expression с помощью 3>&1. Кажется, что команды в блоке кода, записывающие в другие выходные потоки (подробный, отладочный), не фиксируются даже путем добавления параметров 4>&1 и 5>&1 к Invoke-Expression. Однако поток # 2 (ошибки) правильно фиксируется Invoke-Command, как показано выше.

...