PowerShell: захват результатов внешнего процесса, который записывает в stderr переменную - PullRequest
4 голосов
/ 18 ноября 2011

Мне нужно записать вывод внешнего процесса в переменную (строку), чтобы я мог выполнить некоторую обработку этого.

Ответ от здесь работает хорошо, пока процесс пишет в стандартный вывод.Однако, если процесс завершается неудачей, он записывает в stderr.Я также хотел бы захватить эту строку, и я не могу понять, как это сделать.

Пример:

$cmdOutput = (svn info) | out-string

Это работает, если SVN не имеет ошибки.Если произошла ошибка, SVN записывает в stderr, а $cmdOutput пусто.

Как записать текст, записанный в stderr, в переменную PowerShell?

Ответы [ 2 ]

5 голосов
/ 18 ноября 2011

Попробуйте это:

$cmdOutput = svn info 2>&1
1 голос
/ 24 июля 2017

В дополнение к полезному ответу manojlds с обзором:

Чтобы кратко объяснить выражение перенаправления 2>&1:
2> перенаправляет (>) выходной поток PowerShell error с номером 2 (который отображается на stderr) в (&) выходной поток PowerShell success *1015*, чей номер 1 (и отображается на стандартный вывод).
Запустите Get-Help about_Redirection, чтобы узнать больше.

Захват вывода stdout и stderr вместе в виде объединенного потока:


Как набор выходных строк как строки , без возможности определить, какая строка пришла из какого потока:

Использование встроенной в платформу оболочки для слияния в источнике заставляет PowerShell видеть только вывод stdout , который, как обычно, собирается в массив строк.

В следующих примерах каждая команда создает выходные данные как stdout, так и stderr.
Все команды используют синтаксис PSv3 + для удобства и краткости.

Windows Пример:

# Collect combined output across both streams as an array of
# strings, where each string represents and output line.
# Note the selective quoting around & and 2>&1 to make sure they
# are passed through to cmd.exe rather than PowerShell itself interpreting them.
$allOutput = cmd /c ver '&' dir \nosuch '2>&1'

Пример Unix (PowerShell Core):

# sh, the Unix default shell, expects the entire command as a *single* argument.
$allOutput = sh -c '{ date; ls /nosuch; } 2>&1'

Примечание:

  • Вам необходимо явно вызывать оболочку платформы (cmd /c в Windows, sh -c в Unix), что делает этот подход менее переносимым.

  • По полученному массиву строк вы не сможете определить, какая строка пришла из какого потока.
    Ниже описано, как сделать это различие.


Как смесь строки строк (из stdout) и [System.Management.Automation.ErrorRecord] "строк" (из stderr):

Используя перенаправление PowerShell 2>&1, вы также получаете единый набор строк, представляющих объединенные потоки stdout и stderr, но строки stderr не фиксируются как строки , но как [System.Management.Automation.ErrorRecord] экземпляры.

Предупреждение : ошибка в Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.4 приводит к неожиданному поведению при перенаправлении потока ошибок PowerShell с помощью 2>, пока действует $ErrorActionPreference = 'Stop' - см. этот выпуск GitHub .

Это дает вам возможность различать строки stdout и stderr, проверяя тип данных каждого элемента массива.
С другой стороны, вам может потребоваться преобразовать строки stderr в строки.

В PSv6 различные типы данных не очевидны, когда вы просто выводите захваченный вывод, но вы можете определить это по отражению:

# Let PowerShell merge the streams with 2>&1, which captures
# stdout lines as strings and stderr lines as [System.Management.Automation.ErrorRecord] 
# instances.
$allOutput = cmd /c ver '&' dir \nosuch 2>&1

Проверьте результат:

PS> $allOutput | % GetType | % Name
String
String
String
String
String
String
ErrorRecord

Как видите, последний элемент массива - это запись об ошибке, представляющая одну строку вывода File Not Found stderr, созданную командой dir \nosuch.

Примечание: вплоть до PSv5.1, когда вы выводите захваченный вывод на консоль, экземпляры [System.Management.Automation.ErrorRecord] фактически отображаются в том же формате, что и ошибки PowerShell, возможно, из-за этого создается впечатление, что произошла ошибка затем .
В PSv6 эти записи об ошибках печатают только свое сообщение, то есть содержимое исходной строки stderr, и визуально вы не можете сказать, что печатается запись об ошибке, а не строка.

В преобразовать весь захваченный вывод в строки :

$allOutput = cmd /c ver '&' dir \nosuch 2>&1 | % ToString

To отфильтровать строки stderr (и в процессе конвертировать их в строки):

$allOutput = cmd /c ver '&' dir \nosuch 2>&1
$stderrOnly = $allOutput | ? { $_ -is [System.Management.Automation.ErrorRecord] } | 
  % ToString

Захват вывода stderr отдельно :


Использование временного файла :

Начиная с PSv5.1, единственный прямой способ захвата выходных данных stderr - это использовать перенаправление 2> с filename target; то есть, чтобы захватить вывод stderr - как текст - в файле :

$stderrFile = New-TemporaryFile # PSv5+; PSv4-: use [io.path]::GetTempFileName()
$stdoutOutput = cmd /c ver '&' dir \nosuch 2>$stderrFile
$stderrOutput = Get-Content $stdErrFile
Remove-Item $stderrFile

Очевидно, что это громоздко и также медленнее, чем операции в памяти.


Использование оператора сбора PSv4 + .Where() :

(Малоизвестный) оператор коллекции PSv4 + .Where() позволяет разбить коллекцию на две части в зависимости от того, пройдены ли элементы булева проверка или нет:

 # Merge the streams first, so they go to the success stream, then
 # split the objects in the merged stream by type.
 $stdoutOutput, $stderrOutput = (cmd /c ver '&' dir \nosuch 2>&1).Where({
   $_ -isnot [System.Management.Automation.ErrorRecord]
 }, 'Split')

 # Convert the collected [System.Management.Automation.ErrorRecord]
 # instances to strings.
 $stderrOutput = $stderrOutput | % ToString

Потенциальная будущая альтернатива:

В этом выпуске GitHub предлагается новый синтаксис перенаправления, который позволит собирать строки stderr в переменной намного проще:

 # As of v5.1: WISHFUL THINKING
 $stdout = cmd /c ver '&' dir \nosuch 2>&stderr # collect stderr lines in var. $stderr
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...