Примітка. Команда у запитанні використовує Start-Process
, що запобігає прямому захопленню результатів цільової програми. Як правило, не використовуйте Start-Process
консольні програми синхронно - просто викликайте їх безпосередньо , як у будь-якій оболонці. Це підтримує додаток підключеним до стандартних потоків викликової консолі, дозволяючи фіксувати його вихід простим призначенням $output = netdom ...
, як детально описано нижче.
По суті , захоплення виводу з зовнішніх утиліт працює так само, як і з нативними командами PowerShell (можливо, вам потрібно оновити, як виконувати зовнішні інструменти ):
$cmdOutput = <command> # captures the command's success stream / stdout
Зверніть увагу, що $cmdOutput
отримує масив об'єктів, якщо <command>
виробляє більше 1 вихідного об'єкта , що у випадку зовнішньої програми означає масив рядків, що містить вихідні рядки програми .
Якщо ви хочете $cmdOutput
завжди отримувати один - потенційно багаторядковий - рядок , використовуйте
$cmdOutput = <command> | Out-String
Для отримання виводу в змінну та друку на екрані :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
Або, якщо <command>
це командлет або розширені функції, ви можете використовувати загальний параметр
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
Зверніть увагу , що з -OutVariable
, в відміну від інших сценаріїв, $cmdOutput
це завжди колекція , навіть якщо тільки один об'єкт виводиться. Зокрема, [System.Collections.ArrayList]
повертається екземпляр типу масиву .
Дивіться цей випуск GitHub для обговорення цієї невідповідності.
Щоб захопити вихід з декількох команд , використовуйте або субекспресію ( $(...)
), або викликайте блок сценарію ( { ... }
) з &
або .
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
Зауважте, що загальна потреба префіксувати &
(оператор виклику) окрему команду, ім'я / шлях якої є цитований - наприклад,$cmdOutput = & 'netdom.exe' ...
- не пов'язана із зовнішніми програмами такому (церівній мірі відноситьсядо скриптів PowerShell), але синтаксис вимога : PowerShell аналізує оператор, якийза замовчуваннямпочинається з цитованого рядка в режимі виразів , тоді як режим аргументів потрібен для виклику команд (командлети, зовнішні програми, функції, псевдоніми), що ізабезпечуєте, що.&
Ключова відмінність між $(...)
та & { ... }
/ . { ... }
полягає в тому, що перший збирає весь вхід у пам'ять перед поверненням у цілому, тоді як другий передає вихід, придатний для обробки конвеєра по одному.
Перенаправлення також принципово працюють (але див. Застереження нижче):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
Однак для зовнішніх команд наступні шанси спрацюють так, як очікувалося:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
Міркування, характерні для зовнішніх програм:
Зовнішні програми , оскільки вони працюють поза системою типів PowerShell, повертають рядки лише через їхній потік успіху (stdout).
Якщо вихід містить більше 1 рядка , PowerShell за замовчуванням розбиває його на масив рядків . Точніше, вихідні рядки зберігаються в масиві типу[System.Object[]]
, елементами якого є рядки ([System.String]
).
Якщо ви хочете, щоб результат був одиночним , потенційно багаторядковим рядком , перейдіть доOut-String
:
$cmdOutput = <command> | Out-String
Перенаправлення stderr на stdout з 2>&1
, щоб також захопити його як частину потоку успіху, поставляється із застереженнями :
Щоб зробити 2>&1
злиття stdout та stderr біля джерела , дозвольте cmd.exe
обробляти перенаправлення , використовуючи такі ідіоми:
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = cmd /c <command> '2>&1' | Out-String # single string
cmd /c
викликає cmd.exe
команду <command>
та завершує роботу після<command>
завершення.
- Зверніть увагу на єдині цитати навколо
2>&1
, що забезпечує перехід до переадресаціїcmd.exe
а не інтерпретується PowerShell.
Зауважте, що залучення cmd.exe
означає, що його правила для виходу з символів та розширення змінних оточуючих середовищ починають грати за замовчуванням, крім власних вимог PowerShell; в PS v3 + ви можете використовувати спеціальний параметр --%
(так званий символ стоп-синтаксичного аналізу ), щоб вимкнути інтерпретацію інших параметрів PowerShell, за винятком cmd.exe
посилань на зміну середовища типу, таких як %PATH%
.
Зауважте, що оскільки ви поєднуєте stdout та stderr у джерелі за допомогою цього підходу, ви не зможете розрізнити лінії, що виникла stdout та stderr. в PowerShell; якщо вам потрібна ця відмінність, використовуйте власне 2>&1
перенаправлення PowerShell - див. нижче.
Використовуйте PowerShell 2>&1
перенаправлення щоб дізнатися, які рядки надходили з якого потоку :
Stderr вихід захоплюється в якості записів про помилки ( [System.Management.Automation.ErrorRecord]
), а не рядки, так що вихідний масив може містити суміш з рядків (кожен рядок , що представляє собою стандартний висновок рядка) і записи про помилки (кожен запис , що представляє Stderr лінію) . Слід зазначити, що в відповідно до проханням 2>&1
, як рядки і приймаються через PowerShell в запису про помилки успіху вихідний потік).
У консолі записи про помилки друкуються червоним кольором , а 1-й за замовчуванням створює багаторядковий дисплей у тому ж форматі, що відображатиметься непомітна помилка cmdlet; наступні записи про помилки друкуються також червоним кольором, але друкують лише повідомлення про помилку в одному рядку .
Під час виведення на консоль рядки зазвичай надходять спочатку у вихідний масив, після чого записуються помилки (принаймні серед партії рядків stdout / stderr, що виводяться "в той же час"), але, на щастя, коли ви фіксуєте вихід , він правильно переплетений , використовуючи той самий порядок виводу, який ви отримали б без 2>&1
; іншими словами: при виході на консоль виході захоплений вихід НЕ відображає порядок, у якому рядки stdout та stderr були створені зовнішньою командою.
Якщо ви зафіксували весь результат у одній строці за допомогоюOut-String
, PowerShell додасть додаткові рядки , оскільки рядкове представлення запису про помилки містить додаткову інформацію, таку як розташування ( At line:...
) та категорія ( + CategoryInfo ...
); Що цікаво, це стосується лише першого запису про помилки.
- Щоб обійти цю проблему, застосувати
.ToString()
метод до кожного вихідного об'єкту замість трубопроводу до Out-String
:
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
у PS v3 + ви можете спростити:
$cmdOutput = <command> 2>&1 | % ToString
(Як бонус, якщо вихід не захоплений, це дає належний переплетений вихід навіть під час друку на консоль.)
Як альтернативи, фільтрувати записи про помилки Out і відправити їх в потік помилок PowerShell зWrite-Error
( в якості бонусу, якщо вихід не враховуються, це призводить до правильно перемежовується вихід навіть при друку на консоль):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
для виконання (за визначенням зовнішніх) консольних програм синхронно - просто викликайте їх безпосередньо , як у будь-якій оболонці; а саме:netdom /verify $pc /domain:hosp.uhhg.org
. Це підтримує додаток підключеним до стандартних потоків викликової консолі, дозволяючи захоплювати його вихід простим призначенням$output = netdom ...
. Більшість відповідей, наведених нижче, неявно відмовляютьсяStart-Process
на користь прямого виконання.