Log what is deleted - powershell

I'm very new to this but I have a problem with deleting 30days old files which I found an answer to here: Powershell - Delete subfolder(s) in folder with specific name(s) older than 30 days
But I would like to make a follow question on that.
The code I use is this:
gci P:\ -directory -recurse | ?{$_.FullName -match ".:\\.+?\\.+?\\.+?\\.+?\\.+?\\" -and $_.CreationTime -lt (get-date).AddDays(-30)}|Remove-Item -recurse -whatif
Is it possible to log what is deleted as well? Would be awesome if the size of the files are included in the log file. Thanks!

Make the remove operation verbose and redirect the verbose stream to a file:
... | Remove-Item -Recurse -Verbose 4> 'C:\path\to\your.log'
Note that this requires at least PowerShell v3.
If you only want to log what would be deleted without actually deleting it, use -WhatIf instead of -Verbose:
... | Remove-Item -Recurse -WhatIf
You can also combine the two:
$dryrun = $true # set to $false to actually delete
... | Remove-Item -Recurse -Verbose -WhatIf:$dryrun 4> 'C:\path\to\your.log'
However, -WhatIf output goes to the host console, so it can't be redirected to a file. You could use Start-Transcript as a workaround, but that would record everything, not just the would-be deletes. Or you could run the entire code/script (without redirections) in a new PowerShell process:
powershell.exe -File 'C:\path\to\your.ps1' > 'C:\path\to\your.log'
The host output of a child PowerShell process is merged into its STDOUT, so you can redirect it "from the outside".

Related

How do I remove-items in a folder without the confirm box popping up? [duplicate]

This question already has an answer here:
How do I automatically answer “yes” in Powershell?
(1 answer)
Closed 4 months ago.
I want to deleted all the files in the temp folder... any .zip files , .txt files and any folder files including whatever is inside each of those folders (everything). I thought this would be simple but so far my script keeps getting the confirmation pop-up asking if I want to delete all these child items. I tried using -confirm:$false but that doesn't seem to work. I appreciate any suggestions. Thank you.
$list = Get-ChildItem -directory "C:\temp\*" -Include * -Name
get-childitem c:\temp -Include #(get-content $list) | Remove-Item -Force -whatif
I tried using the -confirm:$false argument as well as the -force with no luck.
You want -path vs -directory.
#Looking for Temp under Windows:
$list = (Get-ChildItem -path "C:\*\Temp*\*.*").FullName | Remove-Item -Force
#Looking for Temp under Root:
$list = (Get-ChildItem -path "C:\Temp*\*.*").FullName | Remove-Item -Force
If your Temp dirs have subs you could also add the -recurse switch.
To avoid confirmation requests add the parameter -confirm:$false. If you want to include everything don't specify the parameter -include * - the default is to return everything, no need to slow down due to unecessary filters.
just do:
get-childitem -path C:\temp -directory | remove-item -force -confirm:$false -recurse
btw. u did specify the parameter -directory, currently only directories are returned by get-childitem, so files directly stored in C:\temp would remain.

Delete multiple files using powershell and a .txt file

I have a .txt with the names of over 1000 files I want to delete. My .txt file does not have file paths. The files I want to delete are spread throughout multiple folders but they are all in the same drive. Is there any way to use powershell or command prompt to search for all files within my drive with the same name as what is listed in my .txt file and delete them?
Assuming you're PowerShell prompt is currently set at the root location from which you want to start your search and the file is in the same directory:
gc .\MyListOfFilesIWantToDelete.txt | %{gci $_ -Recurse | Remove-Item -WhatIf}
Note, you'll have to remove the -whatif
Or, let's say your file is somewhere else where you have PowerShell opened (eg: ~/Documents), and you want to scan your D: drive. This should work:
gc .\MyListOfFilesIWantToDelete.txt | %{gci D:\ -Filter $_ -Recurse -ErrorAction SilentlyContinue | Remove-Item -WhatIf}
Note I put SilentlyContinue. This is because you'll see a lot of red if you don't have access to folders in your search path.
Alternatively, you can load up a variable with your list of files..
$thesefiles = gc .\mylistoffilesiwanttodelete.txt
.. and use the Remove-Item cmdlet directly..
Remove-Item -Path D:\Folder -Include $thesefiles -Recurse -WhatIf
or in one swoop without loading a variable:
Remove-Item -Path D:\Folder -Include $(gc .\mylistoffilesiwanttodelete.txt) -Recurse -WhatIf
Again, I'm using -WhatIf for testing. Also, I've noticed different behaviors in the past with get-childitem on different versions of PowerShell. I tested these with 5.1
Change directory from following powershell command
Following command will allow you to delete .txt files in specific directory
Get-ChildItem C:\*.txt -file -r | remove-item

Suppressing confirmation on Remove-Item WITHOUT using -recurse

So I want to delete top level directories based on creation date. I don't want my script to consider ANY dates below that (e.g. my top level folder might be 31 days old, whereas subdirectories may be newer - but I still want the whole top level directory removed along with everything in it - I only care about the top level folder's creation date. Thus I don't want to use -recurse. However, I keep getting prompted with the whole "the folder has subdirectories do you want to continue...blah blah". Can't have that. How can I suppress this without using -recurse? I've tried Remove-Item -Force -confirm:$false as well as Remove-Item -Force -ea 0 to no avail. (PS -ea 0 is short for -ErrorAction SilentlyContinue) Thanks!
Get-ChildItem -Path e:\myfolder -Force | Where-Object { $_.PSIsContainer -and $_.CreationTime -lt (Get-Date).AddDays(-29) } | Remove-Item -Force

Searching and deleting registry entries using wildcards

Is it possible to search for a wildcard - example *WAAgent* or *WAHost* and delete every registry key that references that wildcard statement above?
You may try something like:
Get-ChildItem -Path HKLM:\ -Recurse -Include *WAAgent* -ErrorAction SilentlyContinue | Remove-Item
Get-ChildItem -Path HKLM:\ -Recurse -Include *WAHost* -ErrorAction SilentlyContinue | Remove-Item
You have to specify in -Path if they are location in HKLM(local machine) or HKCU(current user), as they are two different drives. This has to be run as admin, and will give lots of errors(that's why I used -ErrorAction SilentlyContinue to hide them).
CAUTION: Personally I don't think it's smart to be using wildcards in registry though since it may delete something you didn't know about that could crash the system. My recommendation would be to compile a list of paths to the keys you want to remove and loop through it with foreach to delete one by one. Again, wildcards are DANGEROUS in registry.
If you are searching for a property value instead of a key value (and delete the relative key) you can use something like this:
gci HKLM: -rec -ea SilentlyContinue | % { if((get-itemproperty -Path $_.PsPath)
-match "WAAGent") { $_.PsPath} } | Remove-Item
Like for the #Graimer's answer, BE CAREFULL!!!
As all have already suggested, use this with extremely caution!! The following will go through all registry hives. Keep in mind that a matching key found can have a deep structure underneath it and you're deleting it all. Remove the WhatIf switch to actually delete the keys.
Get-ChildItem Microsoft.PowerShell.Core\Registry:: -Include *WAAgent*,*WAHost* -Recurse |
Remove-Item -Recurse -Force -WhatIf
I had the problem that there was a Session ID in the path of the registry.
To solve this is got the first part of the registry, stored it in a variable and used this for my foreach loop where the keys for drive mappings were stored.
The above was too rigorous in my case.
The below shows an example to remove (local) drive mappings in a session (the problem i had).
Start-Sleep -Seconds 20
# This stores the Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\<SESSIONID>"
$SessionInfo = Get-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\*"
cd "HKCU:\"
Start-Sleep -Seconds 1
$Items = Get-ChildItem "$SessionInfo\MyComputer\Namespace"
foreach($Item in $Items){
Remove-Item $Item -Force -Recurse -Verbose
}

PowerShell remove-item succeeds but throws exception

I'm running the following command in a directory that is the root of a Mercurial repository. I want to delete any files and folders beginning with ".hg":
gci -rec -filter ".hg*" | remove-item -recurse -force
The strange thing, is that it does actually work, but still produces the following exception:
Get-ChildItem : Could not find a part of the path 'C:\temp\WSCopyTest\MyCompany.Services.DaftPunk\.hg\'.
At line:1 char:4
+ gci <<<< -rec -filter ".hg*" | remove-item -recurse -force
+ CategoryInfo : ReadError: (C:\temp\WSCopyT...es.DaftPunk\.hg:String) [Get-ChildItem], DirectoryNotFoundException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand
Because the exception is thrown by Get-ChildItem, I suspect my understanding of the pipelining in PowerShell is flawed. Almost like Get-ChildItem finds an item, passes it to the next cmdlet in the pipeline, then looks for the next one? Is that how it works?
The following was supposed to be a script that would replicate the problem, but, on the same machine, it works flawlessly:
$repoRoot = "C:\Temp\HgDeleteTest\MyRepo"
remove-item $repoRoot -recurse -force
md $repoRoot
pushd $repoRoot
hg.exe init
add-content ShouldBeLeftBehind.txt "This is some text in a file that should be left behind once the various .hg files/directories are removed."
add-content .hgignore syntax:glob
add-content .hgignore *.dll
add-content .hgignore *.exe
hg.exe commit --addremove --message "Building test repository with one commit."
gci -rec -filter ".hg*" | remove-item -recurse -force
popd
Could it be that you're removing a folder starting with .hg, which in turn contains a file starting with .hg, but that file no longer exists?
I expect Antony is correct - using the -recurse on the gci will enumerate both files and directories that match ".hg*". The directory will be returned first. Then remove-item deletes the directory first, and the -force switch deletes all the files in it. Then it tries to delete the files that were in that directory that match ".hg*" (that were there when the gci ran) but they're gone now. This should stop the errors:
gci ".hg*" -recurse | select -expand fullname | sort length -desc | remove-item -force
Sorting the full names in descending order of length insures that no matched parent directory is deleted before all the matched files in that directory are deleted.
The following will restrict its deletions to files only, and exclude folders.
gci -rec -filter ".hg*" | Where {$_.psIsContainer -eq $false} | remove-item -recurse -force
Then run it again, by deleting the folders:
gci -rec -filter ".hg*" | Where {$_.psIsContainer -eq $true} | remove-item -recurse -force
I ended up separating the search from the removal. I believe my issue was to do with the way piping works by default (i.e. one item at a time) but I've not been able to build a test to check it.
In any case, the following works fine:
$hgObjects = gci -rec | ?{ $_.name -like ".hg*" -and $_.name -ne ".hgignore" }
remove-item $hgObjects -force -recurse
Using this style, the remove-item cmdlet gets an array of items found and works through them.
The .hg folder in my original example didn't have anything in it called .hg*, so I don't see what was going on. It felt more like it was trying to delete the folder twice, which I find very strange. I wonder if it's actually a PowerShell manifestation of this standard .NET behaviour:
An enumerator remains valid as long as the collection remains
unchanged. If changes are made to the collection, such as adding,
modifying or deleting elements, the enumerator can be invalidated and
the next call to MoveNext or Reset can throw an
InvalidOperationException. If the collection is modified between
MoveNext and Current, Current returns the element that it is set to,
even if the enumerator is already invalidated.
(Taken from http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.getenumerator(v=vs.71).aspx).