Reputation: 33
I need to edit a script that I found here so that I can first see a report of the files it will delete including File name and path along with the "LastWriteTime" property so that I can analyze the output of the script for a couple of months before executing and before I configure it as a scheduled task:
I have tried playing around with the LastWriteTime object property for a while but I dont know what else to do..
Below is the code:
$limit = (Get-Date).AddDays(-30)
$del30 = "D:\contoso_ftp\users"
$ignore = Get-Content "C:\Users\username\Documents\Scripts\ignorelist.txt"
Get-ChildItem $del30 -Recurse |
Where-Object {$_.LastWriteTime -lt $limit } |
Select-Object -ExpandProperty FullName |
Select-String -SimpleMatch -Pattern $ignore -NotMatch |
Select-Object -ExpandProperty Line |
Remove-Item -Recurse -WhatIf
This is what the -Whatif output looks like so far:
What if: Performing the operation "Remove File" on target "D:\contoso_ftp\users\ftp-contosoaccount\contoso Downloads\contosofile.zip".
So.. as you can see I need to be able to get the "LastWriteTime" property in there.
Any help or pointers into articles or documentation is greatly appreciated.
Thanks in advance,
//Lennart
Upvotes: 3
Views: 835
Reputation: 437953
As Mathias R. Jessen notes, you have no control over the target-object descriptions that cmdlets use with the common -WhatIf
parameter, PowerShell's dry-run feature.
Here's a workaround, but note that it is suboptimal, mostly because use of a ForEach-Object
call with per-object execution of a script block slows the command down noticeably:
'file1.txt', 'file2.txt' | # Stand-in for everything before your Remove-Item call
Get-Item | # transform strings into [System.IO.FileInfo] objects
ForEach-Object {
$whatIf = $true # Set this to $false later to perform actual deletion.
# Prepend the last-write timestamp *without a trailing newline*
if ($whatIf) { Write-Host -NoNewline "$($_.LastWriteTime): " }
# Let Remove-Item append its what-if message
$_ | Remove-Item -WhatIf:$whatif
}
The output will look something like this:
02/07/2021 15:44:24: What if: Performing the operation "Remove File" on target "/Users/jdoe/file1.txt".
02/07/2021 15:44:24: What if: Performing the operation "Remove File" on target "/Users/jdoe/file2.txt".
Note:
If you want to control the -WhatIf
flag from outside your script, you have two options:
Either: Decorate your script's param(...)
block with a [CmdletBinding(SupportsShouldProcess)]
attribute to turn on -WhatIf
common-parameter support:
Caveat: This will make calls to all cmdlets in your script that support -WhatIf
apply it implicitly when you pass -WhatIf
to your script, though you can selectively suppress that with -WhatIf:$false
; see the alternative below for the inverse approach.
Since PowerShell automatically translates this common parameter into a local $WhatIfPreference
preference variable, the code above must be adapted:
if ($WhatIfPreference)
to test if -WhatIf
was passed and Write-Host
output should be produced.-WhatIf:$whatIf
from the Remove-Item
call - if -WhatIf
is passed to your script, it will apply automaticallyOr: If you want more control, use a custom [switch] $WhatIf
parameter declaration instead of SupportsShouldProcess
, and query $WhatIf
/ apply -WhatIf:$WhatIf
explicitly and selectively.
$whatIf = $true
statement from the code above.$WhatIfPreference
variable, unless you explicitly check for it in case -WhatIf
wasn't explicitly passed (check with $PSBoundParameters.ContainsKey('WhatIf')
).You can get as verbose as you like with the inline Write-Host
call, but note that single-line output is only possible if you prepend the Write-Host
output.
Alternatively, adapting Daniel's approach from a comment on the question, you can adapt the if
statement above to bypass the Remove-Item -WhatIf
call altogether if $whatIf
is set to $true
in the script block, and craft the full what-if message yourself:
if ($whatif) { Write-Host ... } else { $_ | Remove-Item }
Note that -WhatIf
output can neither be captured nor suppressed - it always prints directly to the host (console).
Upvotes: 1
Reputation: 27766
You can't modify the output of Remove-Item
when run with -WhatIf
but you could wrap your pipeline in a function that supports -WhatIf
and provides its own customized -WhatIf
output:
function Test-Something {
[CmdletBinding(SupportsShouldProcess)] # This enables -WhatIf processing
param (
[string] $path,
[datetime] $limit,
[string] $ignore
)
Get-ChildItem $path -Recurse |
Where-Object { $_.LastWriteTime -lt $limit } |
Select-Object -ExpandProperty FullName |
Select-String -SimpleMatch -Pattern $ignore -NotMatch |
Select-Object -ExpandProperty Line |
Get-ChildItem -Recurse | ForEach-Object {
# If -WhatIf is passed, output the message and skip the deletion.
# Else enter the if-block and delete the file.
if( $PSCmdlet.ShouldProcess(
"Performing the operation `"Remove File`" on target `"$_`" (LastWriteTime: $($_.LastWriteTime))",
"Are you sure you want to delete file `"$_`" ?",
"Confirm" ) ) {
Remove-Item $_
}
}
}
Note that I didn't have to define the -WhatIf
parameter within the function because it is added automatically by specifying SupportsShouldProcess
. Now you can pass -WhatIf
to your function to do a dry run:
Test-Something $del30 $limit $ignore -WhatIf
Sample output:
What if: Performing the operation "Remove File" on target "C:\test\file1.txt" (LastWriteTime: 12/30/2020 19:47:10)
What if: Performing the operation "Remove File" on target "C:\test\file2.txt" (LastWriteTime: 01/12/2021 00:59:59)
I've used the $PSCmdlet.ShouldProcess
overload that takes three arguments to provide a detailed -WhatIf
message. The other two arguments provide a message and a headline for the related -Confirm
argument, which is also supported by the function:
Test-Something $del30 $limit $ignore -Confirm
Now PowerShell will ask before deletion:
Confirm
Are you sure you want to delete file "C:\test\file1.txt" ?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Yes"):
Another cool feature you get for free is the verbose output. When you call the function with -Verbose
the message supplied to $PSCmdlet.ShouldProcess()
will be used as the verbose output:
Test-Something $del30 $limit $ignore -Verbose
Output:
VERBOSE: Performing the operation "Remove File" on target "C:\test\file1.txt" (LastWriteTime: 12/30/2020 19:47:10)
VERBOSE: Performing the operation "Remove File" on target "C:\test\file2.txt" (LastWriteTime: 01/12/2021 00:59:59)
Further reading: Everything you wanted to know about ShouldProcess
Upvotes: 4
Reputation: 174505
As you've found, the output from -WhatIf
is rather terse - "Action: Thing" - nothing more.
You can solve this in a script by separating reporting and actual removal:
param(
[string]$Path = "D:\banqsoft_ftp\users",
[datetime]$Limit = $((Get-Date).AddDays(-30)),
[string[]]$ignore = $(Get-Content "$env:USERPROFILE\Documents\Scripts\ignorelist.txt"),
[switch]$Force
)
$targetPaths = Get-ChildItem $del30 -Recurse |
Where-Object {$_.LastWriteTime -lt $limit } |
Select-Object -ExpandProperty FullName |
Select-String -SimpleMatch -Pattern $ignore -NotMatch |
Select-Object -ExpandProperty Line
# List files that would have been affected + their timestamps
Get-ChildItem $targetPaths -Recurse -File |Select-Object FullName,LastWriteTime
if($Force){
# If -Force was specified then we remove as well
$targetPaths |Remove-Item -Recurse -Force
}
Now you can run it as:
PS ~> .\script.ps1
... to just list the files with LastWriteTime
, and once you're confident, run it with -Force
:
PS ~> .\script.ps1 -Force
To actually remove the files
Upvotes: 1