Работа с параметром -WhatIf в PowerShell. Проблема в том, что вывод этой функции по умолчанию отправляется напрямую в консоль, а не в выходной поток. Это усложняет его обработку, особенно если вам нужно программно извлекать данные для логирования или анализа. Давайте разберёмся, как обойти это ограничение.
Почему вывод WhatIf «неуловим»
Когда вы используете -WhatIf
, PowerShell генерирует информационные сообщения, которые попадают в поток информации (Information stream, 6-й поток). Теоретически их можно перехватить через 6>&1
, но на практике это работает не всегда. Например, команда:
Get-ChildItem | Rename-Item -NewName { ... } -WhatIf 6>&1 | ForEach-Object { ... }
…может не вернуть ожидаемых данных. Всё потому, что некоторые командлеты (особенно старые) пишут информацию напрямую в консоль, минуя стандартные потоки. Кстати, это известная особенность, о которой даже упоминают в официальном репозитории PowerShell.
Обходные пути: от запуска отдельного процесса до транскрипции
Самый надёжный способ захватить вывод – запустить операцию в отдельном процессе. Это создаёт изолированное окружение, где консольный вывод можно перехватить «чистым». Например:
$Replace1 = 'Item: C:Test'
$Replace2 = 'What if: Performing the operation "Rename File" on target'
$Command = 'Get-ChildItem -Path .*.pdf | Rename-Item -NewName { $_.Name -replace " ", "_" } -WhatIf'
# Запускаем в новом процессе и сразу чистим вывод
$Result = (& pwsh -Command $Command) -replace $Replace1, '' -replace $Replace2, ''
$Result
Здесь:
pwsh
запускает новый сеанс PowerShell (для версий 5.1 используйтеpowershell.exe
).- Вывод команды сохраняется в переменную
$Result
. - Метод
-replace
удаляет лишние фрагменты текста.
Важный нюанс: Если в путях есть пробелы или спецсимволы, экранируйте их двойными кавычками. Например, путь C:My Folder
станет "C:My Folder"
.
Ещё один метод – Start-Transcript
. Он записывает всё, что происходит в консоли, в файл. Но будьте готовы к тому, что это:
- Требует ручной очистки временных файлов;
- Может захватывать лишние данные (например, приглашение ввода PS);
- Работает только в рамках текущей сессии.
Пример:
Start-Transcript -Path "C:Templog.txt"
Get-ChildItem | Rename-Item -WhatIf { ... }
Stop-Transcript
$Content = Get-Content "C:Templog.txt" | Select-Object -Skip 3 # Пропускаем служебные строки
$Content -replace $Replace1, '' -replace $Replace2, ''
Совет: Если используете транскрипцию, добавьте
-Append
вStart-Transcript
, чтобы не перезаписывать существующие логи.
Частые ошибки и как их избежать
1. Неправильное экранирование кавычек. В примере выше команда передаётся в виде строки. Если в ней есть двойные кавычки, их нужно экранировать обратным слэшем: `"
.
2. Использование Invoke-Expression. Invoke-Expression
часто приводит к путанице с контекстом выполнения. Лучше использовать прямое выполнение или отдельный процесс.
3. Попытка обработать вывод через $_.ToString()
. Объекты в потоке информации могут быть сложными, и их преобразование в строку «на лету» не всегда сохраняет нужный формат.
Вот как выглядит типичная ошибка при работе с Invoke-Expression:
# Так делать не стоит:
Invoke-Expression $VarIvExp | % { $_.Replace($Replace1, '') }
…и как это исправить:
# Правильный вариант:
& pwsh -Command $VarIvExp | ForEach-Object { $_ -replace 'Item:.*\', '' }
На заметку: Если в выводе остаются лишние пробелы или переносы строк, добавьте .Trim()
после замен. Например, $_.Trim()
очистит края строки.
Эти методы могут показаться слегка запутанными, но они работают даже в сложных сценариях (например, при обработке файлов с символами вроде º
или -
). Главное – тестируйте каждую операцию с флагом -WhatIf
перед тем, как выполнять реальные изменения. Когда-нибудь Microsoft добавят нормальный вывод WhatIf в конвейер, но пока приходится импровизировать.