Powershell (PS2) logging Remove-Item - powershell

I'm fairly new to Powershell but manged so far to pull together a script that deletes files older than a defined Created Date and excluding certain file types. However, I'm struggling to incorporate both verbose and file logging output. I've tried various methods I've found online and I think Out-File is the most appropriate however I simply can't make it work. Hoping someone can help!
Set-StrictMode -Version Latest
# $Logfile = "C:\Temp\Log.log"
function Remove-Files([parameter(Mandatory=$true)][ValidateScript({Test-Path $_})][string] $Path, [parameter(Mandatory=$true)][DateTime] $DateTime, [switch] $WhatIf)
{
Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $DateTime -and ($_.Name -notlike "*.txt"-and $_.Name -notlike "*.log")} |
# Out-File -filepath $logfile -append
ForEach-Object { Remove-Item -Path $_.FullName -Force -WhatIf:$WhatIf}
}
Remove-Files -Path "C:\Temp" -DateTime ((Get-Date).AddDays(-10)) # -whatif

You are not sending any content to the log file.
Uncomment the $logfile declaration, and use this for instance:
ForEach-Object {
Remove-Item -Path $_.FullName -Force -WhatIf:$WhatIf
"Removed $($_.FullName)" | Out-File -FilePath $logfile -Append
}

What you want to do is just Tee-Object the files before you remove them. Like, literally just replace your Out-File with Tee-Object:
function Remove-Files {
[CmdletBinding()]
param(
[parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_})]
[string]$Path,
[parameter(Mandatory=$true)]
[DateTime]$DateTime,
# Personally, I would pass the log file as a parameter
# [string]$LogFile,
[switch]$WhatIf
)
Get-ChildItem -Path $Path -Recurse -Force |
Where-Object {
!$_.PSIsContainer -and
$_.CreationTime -lt $DateTime -and
($_.lName -notlike "*.txt" -and $_.Name -notlike "*.log")
} |
Tee-Object -Filepath $LogFile -Append |
Remove-Item -Force -WhatIf:$WhatIf
}
Remove-Files -Path "C:\Temp" -Log "C:\Temp\Rm.log" -DateTime ((Get-Date).AddDays(-10))
The only problem is that:
The output in the log would be formatted the same as if you output to the console, so it's not quite what you'd normally log ...
The log will be the same whether you remove or not (i.e.: -Whatif makes it not delete, but doesn't stop the log)

Here's my updated code to get the logging working..
function Remove-FilesCreatedBeforeDate([parameter(Mandatory=$true)][ValidateScript({Test-Path $_})][string] $Path, [parameter(Mandatory=$true)][DateTime] $DateTime, [string]$LogFile = "C:\Temp\Log.log", [switch] $WhatIf)
{
"LOG START $(Get-Date –f "yyyy-MM-dd HH:mm:ss")" | Out-File -FilePath $logfile -Append
Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $DateTime -and ($_.Name -notlike "*.txt"-and $_.Name -notlike "*.log")} |
ForEach-Object { Remove-Item -Path $_.FullName -Force -WhatIf:$WhatIf
"$(Get-Date –f o) Removed $($_.FullName)" | Out-File -FilePath $logfile -Append | Write-host "Removed $($_.FullName)"}
"LOG END $(Get-Date –f "yyyy-MM-dd HH:mm:ss")" | Out-File -FilePath $logfile -Append
}
Remove-FilesCreatedBeforeDate -Path "C:\Temp" -DateTime ((Get-Date).AddDays(-0))

A while ago I have created a Log-Entry framework where you can do inline logging like:
Remove-Item -Path (Log "Removing:" $_.FullName ?) -Force -WhatIf:$WhatIf

Related

How can I get Powershell to generate logs after moving files?

Hi this is the first time I create a program using powershell, I created a powershell script to move old files that are not in use to a NAS, the code works as what I want, but I need a Log.txt file for find out what files have been moved. can someone please help me?
$Date = Get-Date -UFormat %d-%m-%Y
$Source = 'C:\Source\'
$Temp = 'C:\Backup-Temp\'
$Nas = 'D:\Destination\'
$Ext = "*.zip","*.rar"
$SetTime = '-5'
New-Item -Path $Temp -Name "Backup-$Date" -ItemType "directory"
Foreach ($Ext in $Ext) {
get-childitem -Path "$Source" -Recurse |
Where-object {$_.LastWriteTime -lt (get-date).AddDays($SetTime) -and $_.name -like "$Ext"} |
Move-item -destination "$Temp\Backup-$Date" |
Compress-Archive -Path "$Temp\Backup-$Date" -DestinationPath "$Nas\Backup-$date.Zip"
}
$Date = Get-Date -UFormat %d-%m-%Y
$Source = 'C:\Source\'
$Temp = 'C:\Backup-Temp\'
$Nas = 'D:\Destination\'
$Ext = "*.zip","*.rar"
$SetTime = '-5'
$LogFileFullName = 'c:\tmp\log.txt'
function Write-Log([string]$msg){
Out-File -FilePath $LogFileFullName -InputObject "$([DateTime]::Now): $msg" -Append
}
New-Item -Path $Temp -Name "Backup-$Date" -ItemType "directory"
Foreach ($Ext in $Ext) {
get-childitem -Path "$Source" -Recurse |
Where-object {$_.LastWriteTime -lt (get-date).AddDays($SetTime) -and $_.name -like "$Ext"} |
ForEach-Object {
Move-item $_.FullName -destination "$Temp\Backup-$Date" |
Compress-Archive -Path "$Temp\Backup-$Date" -DestinationPath "$Nas\Backup-$date.Zip"
Write-Log $_.FullName
}
}
You could just add the following to your chain of piped commands:
Add-Content $logfile "$_.name`n"
where $logfile is set to a static filename prior.
I may be old-fashioned, but having so many commands in one chain is prone to failure. It would be more resilient to break-up the chain so that you can include some error checking along the way.
A better but less desirable option would be to put your chain within a try/catch block.
Best of luck.

Add timestamp to output log file before each object

I have a powershell script which deletes files and folders older than 180 days, and I would like to add date and time of deletion in the log file before each object. Is that possible?
$limit = (Get-Date).AddDays(-180)
$path = "D:\RAZMJENA DOKUMENATA"
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force -Verbose 4>&1 | out-file d:\Delete_script\deleted_files_log.txt -append
# Delete any empty directories left behind after deleting the old files.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse -Verbose 4>&1 | out-file d:\Delete_script\deleted_files_log.txt -append
#Delete remaining empty folders older than 180 days.
Get-ChildItem -Path $path -Directory -Recurse | Where {$_.lastwritetime -lt (Get-Date).AddDays($limit) -and (gci $_.fullName).count -eq 0} | Remove-Item -Force -Verbose 4>&1 | out-file d:\Delete_script\deleted_files_log.txt -append
You can do this in you code with add foreachloop
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force -Verbose 4>&1|foreach{($_.Message).Tostring()+" "+((Get-Date).DateTime).ToString()} | out-file d:\Delete_script\deleted_files_log.txt -append
Yes you can add the current date to every line. For the better understanding I would assign a temporary variable. You can do this with every line of code:
$tmp = Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force -Verbose 4>&1
$date = Get-Date -Format "MM/dd/yyyy HH:mm" #Format the Date
"$date --> $tmp" | out-file d:\Delete_script\deleted_files_log.txt -append #Append to logfile
Or even better, create a function that you can call everytime you want to log:
function logToFile($tmp){
$date = Get-Date -Format "MM/dd/yyyy HH:mm" #Format the Date
"$date --> $tmp" | out-file d:\Delete_script\deleted_files_log.txt -append
}
Then you can call it whenever you want:
$tmp = Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force -Verbose 4>&1
logToFile $tmp
If you want to have another format of the date you can get more informations on this page:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date?view=powershell-7

How do I print out the results of my delete script in Powershell

I'm trying to find a way to get my script to print out a log file of what files are actually being deleted. Does anyone know how I could go about doing this? Here is my screen I would like to add that onto.
Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-1) } |
Remove-Item -Force
Unfortunately, the Remove-Item cmdlet doesn't support the -Passthru parameter, which might make this easier. However, you could do this:
Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-1) } |
Tee-Object -FilePath .\Log.txt |
Remove-Item -Force
But that's a bit ugly because it outputs the directory listing from Get-ChildItem. You could do this instead to capture just the paths:
Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-1) } |
Select-Object -ExpandProperty FullName |
Tee-Object -FilePath .\Log.txt |
Remove-Item -Force
Also, note that if you're using PowerShell v3.0 or later, the Get-ChildItem cmdlet supports the -File and -Directory parameters, so you don't need to do the whole -not $_.PsIsContainer stuff.
The overall easiest way if you just need a log would be to use a Transcript and Verbose Output.
your script would look like this, make sure you pay attention to the addition of -Verbose to your command.
Start-Transcript C:\Logs\Remove.txt
Some-Command | Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-1) } | Remove-Item -Force -Verbose
Stop-Transcript
then the C:\Logs\Remove.txt file will have a message like this for each removed file:
VERBOSE: Performing the operation "Remove File" on target "H:\dns.csv".
You would need a loop:
Get-ChildItem -Path $Targets |
Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-1) } |
ForEach-Object {
$_ | Remove-Item -Force
$_.FullName | Out-File -FilePath $LogFile -Append
}
Simplest way to write output to log file is by using -Verbose and then use Write-Verbose or 4> after the command
Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-1) } | Remove-Item -Verbose 4> $log_file_path

Powershell Directory Deletion Positional Parameter error

I've been looking over this script for several days. Worked through a majority of the errors but can't seem to catch what is causing this error. The script is:
. $Env:JobDir\Scripts\DeleteMethods1.ps1
#Purge old logs
Remove-FilesCreatedBeforeDate -Path "$Env:JobDir\Logs\" -DateTime ((Get- Date).AddDays(-30))
#Backup log from last run
if(Test-Path $Env:JobDir\log_*.txt)
{
Move-Item -force $Env:JobDir\log_*.txt $Env:JobDir\Logs\
}
$logFile = $Env:JobDir + "\log_$(Get-Date -Format ""yyyyMMdd-HHmm"").txt"
echo "Removing files older than $Env:Days days" >> $logFile
if($Env:CheckType -eq "Created")
{
# Remove all files created more than 30 days ago
# Optionally use -DeletePathIfEmpty to remove empty directories
Remove-FilesCreatedBeforeDate -Path $Env:PathToCleanUp -DateTime ((Get- Date).AddDays($Env:Days)) -DeletePathIfEmpty "$Env:DeletePathIfEmpty" >> $logFile
}
if($Env:CheckType -eq "Modified")
{
# Delete all files that have not been updated in 30 days
Remove-FilesNotModifiedAfterDate -Path $Env:PathToCleanUp -DateTime ((Get- Date).AddDays($Env:Days)) "$Env:DeletePathIfEmpty" >> $logFile
}
if($Env:CheckType -eq "Accessed")
{
# Delete all files that have not been accessed in 30 days
Remove-FilesNotAccessedAfterDate -Path $Env:PathToCleanUp -DateTime ((Get-Date).AddDays($Env:Days)) "$Env:DeletePathIfEmpty" >> $logFile
}
echo "Done removing files older than $Env:Days days" >> $logFile
cat "$Env:JobDir\log.txt" >> $logFile
The script pulls functions from the following file:
function Remove-EmptyDirectories([parameter(Mandatory=$true)][ValidateScript({Test-Path $_})][string] $Path, [switch] $DeletePathIfEmpty,
[DateTime] $OnlyDeleteDirectoriesCreatedBeforeDate = [DateTime]::MaxValue,
[DateTime] $OnlyDeleteDirectoriesNotModifiedAfterDate = [DateTime]::MaxValue,
[DateTime] $OnlyDeleteDirectoriesNotAccessedAfterDate = [DateTime]::MaxValue)
{
Get-ChildItem -Path $Path -Recurse -Force -Directory | Where-Object { (Get- ChildItem -Path $_.FullName -Recurse -Force -File) -eq $null } |
Where-Object { $_.CreationTime -lt $OnlyDeleteDirectoriesCreatedBeforeDate -and $_.LastWriteTime -lt $OnlyDeleteDirectoriesNotModifiedAfterDate
-and $_.LastAccessedTime -lt $OnlyDeleteDirectoriesNotAccessedAfterDate } |
Remove-Item -Force -Recurse
# If we should delete the given path when it is empty, and it is a directory, and it is empty, and it meets the date requirements, then delete it.
if ($DeletePathIfEmpty -and (Test-Path -Path $Path -PathType Container) -and (Get-ChildItem -Path $Path -Force) -eq $null -and
((Get-Item $Path).CreationTime -lt $OnlyDeleteDirectoriesCreatedBeforeDate) -and
((Get-Item $Path).LastWriteTime -lt $OnlyDeleteDirectoriesNotModifiedAfterDate) -and
((Get-Item $Path).LastWriteTime -lt $OnlyDeleteDirectoriesNotAccessedAfterDate))
{ Remove-Item -Path $Path -Force }
}
# Function to remove all files in the given Path that were created before the given date, as well as any empty directories that may be left behind.
function Remove-FilesCreatedBeforeDate([parameter(Mandatory)][ValidateScript({Test-Path $_})][string] $Path,[parameter(Mandatory)][DateTime] $DateTime,[switch] $DeletePathIfEmpty)
{
Get-ChildItem -Path $Path -Recurse -Force -File | Where-Object { $_.CreationTime -lt $DateTime } | Remove-Item -Force
Remove-EmptyDirectories -Path $Path -DeletePathIfEmpty $DeletePathIfEmpty -OnlyDeleteDirectoriesCreatedBeforeDate $DateTime -OnlyDeleteDirectoriesNotModifiedAfterDate $DateTime -OnlyDeleteDirectoriesNotAccessedAfterDate $DateTime
}
# Function to remove all files in the given Path that have not been modified after the given date, as well as any empty directories that may be left behind.
function Remove-FilesNotModifiedAfterDate([parameter(Mandatory)][ValidateScript({Test-Path $_})][string] $Path, [parameter(Mandatory)][DateTime] $OnlyDeleteDirectoriesNotModifiedAfterDate, [switch] $DeletePathIfEmpty)
{
Get-ChildItem -Path $Path -Recurse -Force -File | Where-Object { $_.LastWriteTime -lt $OnlyDeleteDirectoriesNotModifiedAfterDate } | Remove-Item -Force
Remove-EmptyDirectories -Path $Path -OnlyDeleteDirectoriesNotModifiedAfterDate $OnlyDeleteDirectoriesNotModifiedAfterDate -DeletePathIfEmpty $DeletePathIfEmpty
}
# Function to remove all files in the given Path that were created before the given date, as well as any empty directories that may be left behind.
function Remove-FilesNotAccessedAfterDate([parameter(Mandatory)][ValidateScript({Test-Path $_})][string] $Path, [parameter(Mandatory)][DateTime] $OnlyDeleteDirectoriesNotAccessedAfterDate, [switch] $DeletePathIfEmpty)
{
Get-ChildItem -Path $Path -Recurse -Force -File | Where-Object { $_.LastAccessTime -lt $OnlyDeleteDirectoriesNotAccessedAfterDate } | Remove-Item -Force
Remove-EmptyDirectories -Path $Path -OnlyDeleteDirectoriesNotAccessedAfterDate $OnlyDeleteDirectoriesNotAccessedAfterDate -DeletePathIfEmpty $DeletePathIfEmpty
}
Now the object of this script is to crawl through directories and delete old items. A lot of the variables in here are from environmentally set items. Now when I run this I get an error that states:
Remove-FilesCreatedBeforeDate : A positional parameter cannot be found that
accepts argument ''.
+ Remove-FilesCreatedBeforeDate -Path $Env:PathToCleanUp -DateTime
((Get-Date) ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
+ CategoryInfo : InvalidArgument: (:) [Remove-FilesCreatedBeforeD
ate], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Remove-FilesCreatedB
eforeDate
I figure that I've been looking at this too long and I can't see the error so if it's something simple be kind. Anything as far as advice would be fantastic. Thanks in advance!!!
*Note I am not the one who wrote the script - a co-worker has me on error correction duty.
It has to be a problem with "$Env:DeletePathIfEmpty", since that's the only positional parameter you use in those statements. Based on the other places you call that function I'm guessing you need to change this:
"$Env:DeletePathIfEmpty"
to this:
-DeletePathIfEmpty "$Env:DeletePathIfEmpty"
It looks like that change needs to be made for both the "Modified" and "Accessed" checktype.
Based on your comment pointing out that the -DeletePathIfEmpty parameter is a [switch] it looks like you either need to just call out the -DeletePathIfEmpty switch or modify the function to a [bool] type and pass it either a $true or $false.

Delete files which are not locked (in use)

I'm trying to delete all files (not folders) in %TEMP% which are older than 30 days. The problem is that some files are in use by a program so they can not be deleted. I tried to solve the problem as follow:
function IsFileLocked($filePath){
#write-host $filePath
Rename-Item $filePath $filePath -ErrorVariable errs -ErrorAction SilentlyContinue
$errs.Count
if ($errs.Count -ne 0)
{
return $true #File is locked
}
else
{
return $false #File is not locked
}
}
$Path= "$env:temp"
if ((Test-Path -Path $Path) -ieq $true)
{
$Daysback = '-30'
$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.AddDays($Daysback)
get-childitem $Path -recurse | Where-Object {$_.LastWriteTime -lt $DatetoDelete } |
Where-Object {$_.PSIsContainer -eq $False }| Where-Object {(IsFileLocked -filePath "($_)") -eq $false }# | remove-item -force #-WhatIf
}
The problem is that (IsFileLocked -filePath "($_)") -eq $false doesn't return any element.
Is it possible that get-childitem blocks the files, so that all of them are locked when I run get-childitem?
Any other ideas how to solve this problem?
How about just removing files older than 30 days and ignore the errors:
$old = (Get-Date).AddDays(-30)
Get-ChildItem $env:TEMP -Recurse |
Where-Object {!$_.PSIsContainer -and $_.LastWriteTime -lt $old } |
Remove-Item -Force -ErrorAction SilentlyContinue