Вопрос или проблема
Я пытаюсь захватить и очистить вывод параметра -WhatIf
в PowerShell для операции переименования файла. Цель состоит в том, чтобы удалить некоторые части вывода, но даже после нескольких попыток у меня не получилось это сделать. Ниже представлены методы, которые я попробовал:
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
не давало желаемого результата из-за особенностей захвата этого вывода.
Применение
-
Выполнение в отдельном процессе:
Наиболее практичным методом является запуск команды в новом процессе 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
, выводимого в канале информации. -
Использование 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
так, как необходимо для достижения вашей цели по преобразованию и очистке строки. Каждый из предложенных способов имеет свои достоинства и недостатки, и выбор зависит от конкретной задачи и удобства реализации в вашем сценарии.