0

I want to delete a huge amount of files via PowerShell. I want to use a terminal, because my Explorer crashes if I try to delete or access the folder.

It is an INetCache folder from Windows ("C:\Windows\SysWOW64\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\IE") same for system32.

With the good old cmd I was able to see what is actually in the folder, and there are ~10gb of 0.5 - 1.5 kbit *.htm files. (~8 million files)

Can I use / Are there .net libs for file operations that do not enforce enumerations? all I found in ps is, that most programs are just aliases to .net lib based programs. otherwise I would have to rely on a *.bat file...

Maybe I oversee an easy solution?

What I tried:

  • Remove-Item
  • del (alias Remove-Item)
  • rm (alias Remove-Item)
  • robocopy (mirror from an empty directory to the target directory)
  • Windows Explorer delete (+ shift pressed)

Everything I executed from the PowerShell against the directory froze the session. I think this is because the programs try to enumerate the contents of the directory.

Solutions:

  • A "I dont like it, but it works" solution is using the delete program from the cmd.exe .
kurdy
  • 441
  • 4
  • 15
  • 1
    Could you please provide what you have already tried along with the specific issues you are getting? – I.T Delinquent Jul 09 '19 at 14:43
  • 1
    Looking at the [Directory class's Delete method for .Net](https://learn.microsoft.com/en-us/dotnet/api/system.io.directory.delete?view=netframework-4.8), even that is going to enumerate files and folders, so I don't think there's really a way around this without writing your own binaries to interact with the file system. – TheMadTechnician Jul 09 '19 at 15:40
  • @TheMadTechnician The "Delete" command in the old cmd.exe does not enumerate. This way is neither efficient nor sexy, but you can solve the problem with built-in programs which is okay for me. – kurdy Jul 09 '19 at 15:47
  • @kurdy can you please clarify which "delete" command does not enumerate. Both the "Del" and "RmDir /S" commands enumerates files based on Process Monitor logs. As far as I know its required to enumerate the files to delete them before deleting the containing folder. – Wiz Jul 09 '19 at 20:26
  • @Wiz Can you provide further information? Which terminal is in use? – kurdy Jul 10 '19 at 06:50
  • 1
    If I run CMD.exe. Then, enter the command "del " or "RmDir /S " I see enumeration in the SysInternals Process Monitor logs. https://learn.microsoft.com/en-us/sysinternals/downloads/procmon – Wiz Jul 10 '19 at 12:44
  • Maybe you can avoid the enumeration by passing a wildcard filter argument from within the directory instead of deleting the whole folder? Would be nice to know, but I dont really get ProcMon – kurdy Jul 11 '19 at 07:33

2 Answers2

3

In my experience, I don't know of a method to delete a directory filled with files without enumerating their contents.

That being said, I think you probably went down this path because you found other approaches to be slow, and might be interested in the fastest way to delete files otherwise.

You might want to try this method, modified from this answer The most efficient way to delete millions of files based on modified date, in windows. This method's reliance on the underlying dotnet static classes of [System.IO] should give you the best performance possible.

$HTMLfiles = [System.IO.Directory]::EnumerateFiles("C:\Windows\SysWOW64\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\IE", "*.htm", "AllDirectories")
$date = ([System.DateTime]::Now).AddSeconds(-300)
foreach ($File in $HTMLfiles ) {
    if ([System.IO.File]::GetLastWriteTime($File) -le $date) {
        [System.IO.File]::Delete($File)
    }
} #foreach

Do file deletions always require enumeration?

Reviewing this answer on SO 'How to delete all files and folders in a directory?' lead me to the underlying .net documentation for [System.IO.DirectoryInfo]. After expermienting with the various methods displayed there, I populated a directory with 50K files using this code:

1..50000 | % {
  $i = "c:\temp\SO\Test_$_.htm"
[void][System.IO.File]::Create($i)
}

I then attempted to just flush the directory of contents, using the Directory .Delete() method, like so:

$dir = [System.IO.DirectoryInfo]::new("c:\temp\SO")
measure-command {$dir.Delete($true)}

Watching events on my computer, I could actually see the individual files being deleted one at a time from the directory. I don't see any method to bulk delete files without enumerating them. Heck, even trying to delete the directory doesn't work, as this method also has to delete all files first.

I think it's just something that should be expected when working with an object and API oriented programming and operating system.

FoxDeploy
  • 12,569
  • 2
  • 33
  • 48
  • This is actually good to know and will find its way in future scripts. But my problem was/is, that the enumeration process fails, if there are too many files in the target directory. And powershell offers only enumeration based file operations. Correct me if I'm wrong. Please^^. – kurdy Jul 09 '19 at 15:37
  • 1
    Even if you call the underlying `[System.IO.DirectoryInfo]` static class, you'll find that even that has to recurse through files. Check out my edit for more info. – FoxDeploy Jul 09 '19 at 16:16
1

Just to add to this, in my experience in a Windows Server 2012 R2 trying to delete the system32 inetcache (>15 million files), the erase *.* command from the old cmd.exe (don't use powershell) is the only approach that begins deleting files from the first moment (checked with sysinternal's procmon.exe).

Other alternatives began with a full scan of the files, and just performing a "dir *.*" took 15 hours so it didn't seem like a good approach, specially since I needed to delete in small time slots because the disk queue went too high and greatly affected system performance.

Victor M
  • 26
  • 1