В дополнение к полезному ответу 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