Как обрабатывать и очищать вывод команды -WhatIf в PowerShell?

Вопрос или проблема

Я пытаюсь захватить и очистить вывод параметра -WhatIf в для операции переименования файла. Цель состоит в том, чтобы удалить некоторые части вывода, но даже после нескольких попыток у меня не получилось это сделать. Ниже представлены методы, которые я попробовал:

Set-Location "C:\Path\To\Directory"

$Replace1  = 'Item: C:\Path\To\Directory\'
$Replace2  = 'What if: Performing the operation `"Rename File`" on target'
$Replace3  = 'Destination: C:\Path\To\Directory\'
$VarIvExp  = "GCI -Path . -Filter *.pdf | Ren -New { `$_.Name -replace ' ', '_' -replace '-', '_' } -Whatif ;"

# Различные попытки захватить и заменить части вывода:
Invoke-Expression $VarIvExp            | % { ($_ 3>&1).Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'') }
Invoke-Expression $VarIvExp.ToString() | % { ($_ 3>&1).Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'') }

Invoke-Expression $VarIvExp 3>&1       | % {  $_.ToString().Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'')       }
Invoke-Expression $VarIvExp.ToString() | % { ($_.ToString() 3>&1).Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'') }

Invoke-Expression $VarIvExp.ToString() | % {  $_            | out-string -Replace $Replace1,'' -Replace $Replace2,'' -Replace $Replace3,'' }
Invoke-Expression $VarIvExp            | % {  $_.ToString() |            -Replace $Replace1,'' -Replace $Replace2,'' -Replace $Replace3,'' }

Invoke-Expression $VarIvExp 3>&1       | % {  $_            | out-string -Replace $Replace1,'' -Replace $Replace2,'' -Replace $Replace3,'' }
Invoke-Expression $VarIvExp.ToString() | % {  $_            |            -Replace $Replace1,'' -Replace $Replace2,'' -Replace $Replace3,'' }
Invoke-Expression $VarIvExp            | % {  $_.ToString() |            -Replace $Replace1,'' -Replace $Replace2,'' -Replace $Replace3,'' }

  • Выводы:
What if: Performing the operation "Rename File" on target "Item: C:\Path\To\Directory\SomeText  I nº 001-SMSCBMRJ-20250222.pdf" Destination: C:\Path\To\Directory\SomeText__I_nº_001_SMSCBMRJ_20250222.pdf
What if: Performing the operation "Rename File" on target "Item: C:\Path\To\Directory\SomeText  I nº 002-SMSCBMRJ-20250222.pdf" Destination: C:\Path\To\Directory\SomeText__I_nº_002_SMSCBMRJ_20250222.pdf
What if: Performing the operation "Rename File" on target "Item: C:\Path\To\Directory\SomeText II nº 001-SMSCBMRJ-20250222.pdf" Destination: C:\Path\To\Directory\SomeText_II_nº_001_SMSCBMRJ_20250222.pdf
What if: Performing the operation "Rename File" on target "Item: C:\Path\To\Directory\SomeText II nº 002-SMSCBMRJ-20250222.pdf" Destination: C:\Path\To\Directory\SomeText_II_nº_002_SMSCBMRJ_20250222.pdf

Примечание.: Я также пробовал различные манипуляции непосредственно с $Results, но без успеха. Ниже представлены попытки с $Results$Results.Replace('various attempts','')

Set-Location "C:\Path\To\Directory"
$Replace1  = 'Item: C:\Path\To\Directory\'
$Replace2  = 'What if: Performing the operation `"Rename File`" on target'
$Replace3  = 'Destination: C:\Path\To\Directory\'

$VarIvExp  = "GCI -Path . -Filter *.pdf | Ren -New { `$_.Name -replace ' ', '_' -replace '-', '_' } -Whatif ;"

$Results = Invoke-Expression $VarIvExp

$Results = Invoke-Expression $VarIvExp.Replace( -Replace $Replace1,'' -Replace $Replace2,'' -Replace $Replace3,'')

$Results.ToString().Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'')

  • Желаемый/ожидаемый вывод:
SomeText  I nº 001-SMSCBMRJ-20250222.pdf SomeText__I_nº_001_SMSCBMRJ_20250222.pdf
SomeText  I nº 002-SMSCBMRJ-20250222.pdf SomeText__I_nº_002_SMSCBMRJ_20250222.pdf
SomeText II nº 001-SMSCBMRJ-20250222.pdf SomeText_II_nº_001_SMSCBMRJ_20250222.pdf
SomeText II nº 002-SMSCBMRJ-20250222.pdf SomeText_II_nº_002_SMSCBMRJ_20250222.pdf

Как видно из этой проблемы, вывод -WhatIf не может быть эффективно использован в настоящее время – это предложение о добавлении функции. Он выводится непосредственно в консоль.

Существуют обходные пути, например, этот, который основан на запуске команды как отдельного процесса для захвата прямого вывода, например:

(&pwsh -c $VarIvExp).Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'')

Еще один вариант, который упоминается чаще всего – использование Start-Transcript для временного логирования всего из сессии в файл, но использовать его довольно неудобно.

Ответ или решение

Чтобы эффективно работать с выводом параметра -WhatIf в PowerShell, необходимо понимать его природу и особенности. Параметр -WhatIf предназначен для безопасного выполнения команд, показывая, что произойдет, если команда будет выполнена без фактических изменений. Вывод этого параметра записывается напрямую в консоль и не предоставляется в формате, удобном для дальнейшей обработки, что усложняет задачу манипуляции его выводом.

Теория

При использовании -WhatIf вывод данной команды нельзя поймать или модифицировать стандартными методами захвата вывода, такими как присваивание переменной или использование пайпов. Это объясняется тем, что -WhatIf обрабатывается как информационный канал, а не как стандартный вывод команд. Таким образом, подход к захвату этого вывода требует более изощренных методов, таких как выполнение командного или скриптового блока в отдельном процессе или сессии PowerShell, или использование транскрипции сессии.

Пример

Рассмотрим приведенный пример, где возникает необходимость очищения и манипуляции вывода -WhatIf при попытке переименовать файлы в заданной директории:

Set-Location "C:\Path\To\Directory"

$Replace1  = 'Item: C:\Path\To\Directory\'
$Replace2  = 'What if: Performing the operation `"Rename File`" on target'
$Replace3  = 'Destination: C:\Path\To\Directory\'
$VarIvExp  = "GCI -Path . -Filter *.pdf | Ren -New { `$_.Name -replace ' ', '_' -replace '-', '_' } -Whatif ;"

Здесь целью является замена некоторых частей строки, таких как путь к файлу и служебные сообщения, оставив только старое и новое имена файлов. Однако, как стало понятно, прямое использование методов, таких как Replace(), на выводе -WhatIf не давало желаемого результата из-за особенностей захвата этого вывода.

Применение

  1. Выполнение в отдельном процессе:
    Наиболее практичным методом является запуск команды в новом процессе PowerShell и захват вывода:

    $results = (& "$($env:windir)\System32\WindowsPowerShell\v1.0\powershell.exe" -Command $VarIvExp)
    $cleanResults = $results.Replace($Replace1,'').Replace($Replace2,'').Replace($Replace3,'')
    Write-Output $cleanResults

    В этом примере использование оператора & позволяет выполнить PowerShell как подпроцесс Windows, тем самым получая доступ к непосредственному выводу команды в строковом формате. Это решение обходит ограничение -WhatIf, выводимого в канале информации.

  2. Использование Start-Transcript:

    Start-Transcript -Path "C:\temp\transcript.log"
    & {$VarIvExp}
    Stop-Transcript
    
    $results = Get-Content "C:\temp\transcript.log" |
              Select-String -Pattern "(Прошлое_Имя_Файла.*?)\s+(Новое_Имя_Файла.*?)$" | 
              ForEach-Object { 
                    $_ -replace $Replace1, '' -replace $Replace2, '' -replace $Replace3, ''
              }

    Start-Transcript записывает все действия в сессии PowerShell в лог-файл, что позволяет затем читать файл и извлекать интересующие вас данные.

Вывод

Захват и манипуляция вывода -WhatIf не могут быть выполнены тривиально из-за ограничения с особенностью вывода в информационный поток. Решением является использование методов, позволяющих "обмануть" ограничение, таких как выполнение кода в подчиненной сессии PowerShell или захват вывода через транскрипцию. Эти методы дают возможность обрабатывать вывод -WhatIf так, как необходимо для достижения вашей цели по преобразованию и очистке строки. Каждый из предложенных способов имеет свои достоинства и недостатки, и выбор зависит от конкретной задачи и удобства реализации в вашем сценарии.

Оцените материал
Добавить комментарий

Капча загружается...