Search code examples
windowspowershelloperating-systemsystem

PowerShell Rename or Delete Thumbs.db Files


I am dealing with the Windows bug where you are unable to delete Thumbs.db, as Windows thinks they are "open".

Firstly, I am aware of the possibility of using regedit to allow these to be deleted, but I do not own the network folder so am unable to access the server to do this. However if it helps anyone here is how you would do that:

 add a new "Explorer" Registry path:

 HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Explorer\

 and then add a DWORD entry called "DisableThumbsDBOnNetworkFolders", and set it to 1.

I have found that if I manually rename the Thumbs.db to say "ThisSucks.db" then I can delete the file. I have a ton of these folders with Thumbs in them so I want to write a PS script to rename all of them, or bonus delete.

When I gather the thumbs into a collection, I see the Write-Host print for each file, but when I try to rename I am getting "Cannot rename because file does not exist".

[System.Object[]] $directory = Get-ChildItem -Path $Path -Filter "*.db" -Force -Recurse;

foreach($file in $directory){
    Write-Host -ForegroundColor Green "Found Thumb files";
    Rename-Item $file "StupidFile.db";
} 

Any ideas?


Solution

  • TessellatingHeckler provided the crucial pointer in a comment on the question:

    Perhaps surprisingly, the [System.IO.FileInfo] instances returned by Get-ChildItem bind to Rename-Item's -Path parameter as strings when passed as direct argument, and, in a string context, a [System.IO.FileInfo] instance expands to its mere filename, not the full path.

    Therefore, unless a [System.IO.FileInfo] instance happens to refer to a file in the current directory, Rename-Item won't find it.

    Binding the [System.IO.FileInfo] instances via the pipeline is not subject to this pitfall (the .PSPath property is used for binding to -Path), so you can use the following:

     Get-ChildItem -Path $Path -Filter '*.db' -Force -Recurse |
       Rename-Item -NewName 'StupidFile.sb' -WhatIf
    

    Note the -WhatIf switch, which previews the renaming operations. Remove this switch to perform the actual renaming.


    A note on your first statement, [System.Object[]] $directory = Get-ChildItem ...:

    Rather than casting to [System.Object[]], you can use @(), the array subexpression operator, to ensure that a command's output is treated as an array, even if it returns only a single item (or none):

    $directory = @(Get-ChildItem ...)
    

    That said, on PSv3+ this is typically not necessary, because any scalar value can also be used as if it were an array; e.g.:

    > $scalar = 'a single value'
    
    # Treat $scalar as a collection:
    
    # A scalar as a collection by definition has count 1.
    > $scalar.Count
    1  
    
    # Enumerate the scalar in a foreach loop.
    > foreach ($itm in $scalar) { "[$itm]" }
    [a single value]
    
    # Process the scalar in a pipeline using the ForEach-Object cmdlet.
    > $scalar | ForEach-Object { "[$_]" }
    [a single value]