Работа с параметром -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 в конвейер, но пока приходится импровизировать.
