I'm trying to implement a -WhatIf
flag in a RoboCopy script (swap user
for your user profile):
[CmdletBinding(SupportsShouldProcess)]
param (
$Purge = $false
)
$RCCommand = "Robocopy C:\Users\user\Desktop\Source C:\Users\user\Desktop\Target"
$WhatIfMessage = Invoke-Expression "$RCCommand /L"
if ($PSCmdlet.ShouldProcess($WhatIfMessage, $WhatIfMessage, 'Are you sure?')) {
Invoke-Expression $RCCommand
}
This gives a barely readable output due to mangled formatting:
> .\Desktop\Invoke-RoboCopy.ps1 -WhatIf
What if: ------------------------------------------------------------------------------- ROBOCOPY :: Robust File Copy for Windows ------------------------------------------------------------------------------- Started : Monday, June 2, 2025 19:47:55 Source : C:\Users\user\Desktop\Source\ Dest : C:\Users\user\Desktop\Target\ Files : *.* Options : *.* /L /DCOPY:DA /COPY:DAT /R:1000000 /W:30 ------------------------------------------------------------------------------ 1 C:\Users\user\Desktop\Source\ *EXTRA Dir -1 C:\Users\user\Desktop\Target\NotSource\ *EXTRA Dir -1 C:\Users\user\Desktop\Target\Source1\ ------------------------------------------------------------------------------ Total Copied Skipped Mismatch FAILED Extras Dirs : 1 0 1 0 0 2 Files : 1 0 1 0 0 0 Bytes : 0 0 0 0 0 0 Times : 0:00:00 0:00:00 0:00:00 0:00:00 Ended : Monday, June 2, 2025 19:47:55
For comparison, here's a modified script that just outputs the message directly, bypassing -WhatIf
followed by the output:
[CmdletBinding(SupportsShouldProcess)]
param (
$Purge = $false
)
$RCCommand = "Robocopy C:\Users\user\Desktop\Source C:\Users\user\Desktop\Target"
$WhatIfMessage = Invoke-Expression "$RCCommand /L"
$WhatIfMessage
> .\Desktop\Invoke-RoboCopy.ps1
-------------------------------------------------------------------------------
ROBOCOPY :: Robust File Copy for Windows
-------------------------------------------------------------------------------
Started : Monday, June 2, 2025 19:48:19
Source : C:\Users\user\Desktop\Source\
Dest : C:\Users\user\Desktop\Target\
Files : *.*
Options : *.* /L /DCOPY:DA /COPY:DAT /R:1000000 /W:30
------------------------------------------------------------------------------
1 C:\Users\user\Desktop\Source\
*EXTRA Dir -1 C:\Users\user\Desktop\Target\NotSource\
*EXTRA Dir -1 C:\Users\user\Desktop\Target\Source1\
------------------------------------------------------------------------------
Total Copied Skipped Mismatch FAILED Extras
Dirs : 1 0 1 0 0 2
Files : 1 0 1 0 0 0
Bytes : 0 0 0 0 0 0
Times : 0:00:00 0:00:00 0:00:00 0:00:00
Ended : Monday, June 2, 2025 19:48:19
How do I get the -WhatIf
message to look nice and neat like in the latter example?
Your issue is that the array of strings outputted by robocopy
is getting converted into a single string when passed as argument of .ShouldProcess()
and when this happens in PowerShell, every element of the array gets joined by the value of $OFS
(a space by default). Simple solution is to use a cmdlet that will convert your array to a single string preserving its new-lines, like Out-String
or alternatively, you can join them yourself using -join [Environment]::NewLine
.
As aside, you should probably avoid the use of Invoke-Expression
when not needed, here is how the final code should look:
[CmdletBinding(SupportsShouldProcess)]
param (
$Purge = $false
)
$command = 'Robocopy'
$arguments = @(
'C:\Users\user\Desktop\Source'
'C:\Users\user\Desktop\Target')
$WhatIfMessage = & $command ($arguments + '/L') | Out-String
if ($PSCmdlet.ShouldProcess($WhatIfMessage, $WhatIfMessage, 'Are you sure?')) {
& $command $arguments
}