Как обработать вывод WhatIf в PowerShell: очистка и форматирование

Работа с параметром -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

Здесь:

  1. pwsh запускает новый сеанс PowerShell (для версий 5.1 используйте powershell.exe).
  2. Вывод команды сохраняется в переменную $Result.
  3. Метод -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 в конвейер, но пока приходится импровизировать.

Новое
Интересное