Примітка. Команда у запитанні використовує 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на користь прямого виконання.