Remove-Item with Recurse and Exclude multiple subdirectories - powershell

I am trying to create a script to delete all files and directories within a folder, except for specified folders and their content.
Example tree:
test/
- images/
- folder_to_keep/
- misc/
- blah/
- some_other_folder/
- another_to_keep/
- snafu/
I've searched for this specific question with little luck. I've tried:
Remove-Item .\test\* -Exclude (".\test\images\folder_to_keep", ".\test\some_other_folder\another_to_keep") -Recurse
but that still deletes everything.
FYI, I am trying to run this script in a build job on an Atlassian Bamboo server if that helps anyone. See: https://confluence.atlassian.com/bamboo0515/script-894237366.html. If there is a better way using their options (Shell, Windows PowerShell, /bin/sh or cmd.exe) that would be great too.
Edit: robocopy might be an option also. The basic problem is that I need to mirror the source and the destination paths, but there are folders in the destination that must remain unchanged (they get filled from another process).

-Exclude applies only to leaf elements, not the full path. For example, a filter -Exclude 'foo' would remove a folder named "foo" from the result list, but not its files or child folders.
Combine the parent paths you want excluded in a regular expression and use a regexp (non-)match.
$excludes = "$($pwd.Path)\test\images\folder_to_keep",
"$($pwd.Path)\test\some_other_folder\another_to_keep"
$re = ($excludes | ForEach-Object {[regex]::Escape($_)}) -join '|'
Get-ChildItem -Recurse -Force | Where-Object {
$_.FullName -notmatch "^$re"
} | Remove-Item -Force

Related

How do I remove all folders in a tree of folders that contain no files or folders that contain other files with powershell?

I need a powershell script that will remove all folders in a tree that do not contain files or other folders underneath it that contain files.
I'd love to also be able to optionally qualify this with a file age check to cleanup files as well older than x days and then perform the folder cleanup as well.
The tree could be very complex with many folders within folders and files buried deep within those folders.
It cannot remove a folder that has folders in it that contain files.
Can anyone help me?
Something like this should do you just fine.
# Get all directories beneath $root (whatever your $root may be)
Get-ChildItem -Path $root -Directory -Recurse |
Where-Object {
# Determine if each directory had children
-not (Get-ChildItem -Path $_ -Recurse)
} |
# Remove the -WhatIf if you want to actually remove
Remove-Item -WhatIf
Two additional notes:
The example above includes -WhatIf, so that it doesn't actually delete your files. Simply remove -WhatIf to delete them.
The script does not look for hidden files or folders. To make it do that, add a -Force to each Get-ChildItem

Can't delete Windows folder that contains a subfolder named "..." which contains a subfolder named "..." etc

I'm stumped.
I have a folder called c:/data/ToDelete. It used to contain files as well as a single subfolder called ..., which contained the same files as well as single subfolder named ... and this went on ad nauseum.
I moved the contents of the ToDelete folder elsewhere, but the subfolder/subfolder structure remains, and I can't get rid of it.
This is what it looks like in Explorer
This PC > Data > ToDelete > ... > ... > ... > ... > etc
clicking on the above reveals the path: C:\Data\ToDelete\
I was able to rename the first folder (from c:/data/temp/ to c:/data/ToDelete/) but whenever I try to rename the subfolder I get the "This folder is already open" (probably because it is just referring back to it's parent folder).
Things I've tried (none worked)
the basics: Delete key, Shift Delete, drag to recycle bin, send to recycle bin
command prompt delete, including trying the ASCII code for . just in case.
I tried zipping it with 7zip
I did a chkdsk and got no errors
I tried removing with Revo Uninstaller Pro (the Delete folder option).
I tried renaming the subfolders with this powershell script that I found online thinking I could then delete them but got an error:
Get-ChildItem -Recurse | where {$_.attributes -eq "Directory"} | Where-Object {$_.Name -match '...'} | Rename-Item -NewName { $_.Name -replace '...' , 'ToDelete' }
The funny thing is I found the folder in my backup directory as well, so somehow the entire thing can get copied (which means I have to remove it in multiple places now).
You need to do two things to enable PowerShell to process files or folders literally named ...:
Use -LiteralPath instead of -Path parameter where possible, to prevent PowerShell from parsing the path.
Prepend \\?\ to the full path to tell the underlying Windows APIs to disable all string parsing and to send the string that follows it straight to the file system.
Example:
# For demonstration, create two nested sub folders literally named '...'
New-Item -ItemType Directory -Path '\\?\c:\data\ToDelete\...\...'
# Rename all '...' sub folders to 'ToDelete'
Get-ChildItem -LiteralPath '\\?\c:\data\ToDelete' -Directory -Recurse -Filter '...' |
Sort-Object { $_.Fullname.Length } -Desc |
Rename-Item -NewName ToDelete
Before renaming the folders, we must sort by path length (descending) to make sure that deepest nested folders will be renamed first. Otherwise paths enumerated by Get-ChildItem would be incorrect after renaming the first folder.
For what you have tried:
Note that -match '...' doesn't find folders named literally like "...". The -match operator's RHS operand is a regular expression pattern and as such would match the first sequence of any three characters in the string. The same can be said about -replace '...'. To literally match "...", use -eq '...' or escape the dots like this when used in RegEx pattern: \.\.\.

How to prevent PowerShell -Recurse from renaming first file twice?

When using powershell to rename files with their directory name and file name, my code works, except in the first file in a directory, it gives it two copies of the directory name. So the file book1.xlsx in folder folder1 should become folder1book1.xlsx but it becomes folder1folder1book1.xlsx. The remaining files in folder1 are correctly named folder1book2.xlsx, folder1book3.xlsx, etc.
I have a directory, with many sub-directories. In each sub-dir are files that need their sub-dir name added in.
I've been following this code. For me it looks like:
dir -Filter *.xlsx -Recurse | Rename-Item -NewName {$_.Directory.Name + "_" + $_.Name}
I've also tried
--setting the Recurse -Depth 1 so that it doesn't keep looking for folders in the sub-folders.
--using ForEach-Object {$_ | ... after the pipe, similar to this.
--running it in Visual Studio Code rather than directly in PowerShell, which turns it into:
Get-ChildItem "C:\my\dir\here" -Filter *.xls -Recurse | Rename-Item -NewName {$_.DirectoryName + '_' + $_.Name}
--putting an empty folder inside the sub-directory, setting -Depth 2 to see if that will "catch" the recurse loop
I would expect the files to be named folder1_book1.xlsx, folder1_book2.xlsx, folder1_book3.xlsx.
But all of the attempted changes above give the same result. The first file is named folder1_folder1_book1.xlsx [INCORRECT], folder1_book2.xlsx[CORRECT], folder1_book3.xlsx[CORRECT].
A workaround might be writing an if statement for "not files that contain the sub-directory name" as suggested here. But the link searches for a text string not an object (probably not the correct term) like #_.Directory.Name. This post shows how to concatenate objects but not something like #_.Directory.Name. Having to put in an if statement seems like an unnecessary step if -Recurse worked the way it should, so I'm not sure this workaround gets at the heart of the issue.
I'm running windows 10 with bootcamp on a 2018 iMac (I'm in Windows a lot because I use ArcMap). Powershell 5.1.17134.858. Visual Studio Code 1.38.0. This is a task I would like to learn how to use more in the future, so explanations will help. I'm new to using PowerShell. Thanks in advance!
This was a script I created for one of my customers that may help
<##################################################################################################################################
This script can be used to search through folders to rename files from their
original name to "filename_foldername.extension". To use this script
please configure the items listed below.
Items to Congfigure
-$Original
-$Source
-$Destination
-$Files
Also please change the Out-File date on line 29 to today's date ****Example: 2019-10-02****
We've also added a change log file that is named "FileChange.txt" and can be found in the location identified on line 30
>
$Original="C:\temp\test" #Location of ".cab" files copied
$Source="C:\temp\Test" #Location were ".cab" files are stored
$Destination="C:\temp\Test\2019-10-02" #Location were you want to copy ".cab" files after the file name change. Be sure to change the date to the date you run this script. The script creates a folder with todays date
$Files=#("*.cab") #Choose the file type you want to search for
$ErrorActionPreference = "SilentlyContinue" #Suppress Errors
Get-ChildItem $Original -Include "*.cab" -File -Recurse | Rename-Item -NewName {$_.BaseName+"_"+$_.Directory.Name +'.cab'}
New-Item -ItemType Directory -Path ".\$((Get-Date).ToString('yyyy-MM-dd'))"; Get-ChildItem -recurse ($Source) -include ($Files) | Copy-Item -Destination ($Destination) -EA SilentlyContinue
Get-ChildItem $Original | Where {$_.LastWriteTime -ge [datetime]::Now.AddMinutes(-10)} | Out-File C:\temp\test\2019-10-02\FileChange.txt

Powershell get-childitem exclude directory several levels deep

I have a folder containing changed files to deploy to another server. I am building a list of files below the root, recursively, and I want to skip an entire folder. This is not working:
gci -path \\myfolder -recurse -exclude "*\excludeme\*" | where{! $_PSIsContainer}
It is going ahead and listing all the files in excludeme as well. Even this didn't work:
gci -path \\myfolder -recurse -exclude "\\myfolder\excludeme\*" | where etc.
I should note that "excludeme" is a folder that contains a number of software project folders, i.e. a whole mess of subfolders in itself. Help!
You should be able to use -Exclude if you simple enter the name of the folder:
GCI \\MyFolder -Recurse -Exclude "ExcludeMe"
With that said, if it doesn't work for you (and it wouldn't surprise me, because the File System Provider is horrible at filtering IMHO), you can always filter after you get the results, it will just be a little slower:
GCI \\MyFolder -Recurse | Where{$_.FullName -NotMatch "\\ExcludeMe(\\|$)"}

How can this way exclusion of files be achieved in PowerShell?

I have a PowerShell script on a production server to show me the list of database backup files that are older than 30 days.
I need to see only the files that have the extension of ".bak". However, in my script there is no matching syntax or regular expression, so I am also getting to see a list of files which have an extension like "filename.foo.bak". These files may be text files or other configuration files on the server for which backups have been taken automatically by the program that uses these files.
How do I enable a match filter so that I see only "*.bak" and not other files as mentioned above?
As mentioned by mjolinor, I have used this script to do the exclusion.
gci $paths -recurse -filter *.bak -exclude *.*.bak | ?{!$_.psiscontainer}
However, I have learnt that I need to exclude some system folders such as C:\Windows.
How can this be accomplished as well?
Try this:
gci *.bak -exclude *.*.bak
I tried to reply to your comment, but the code doesn't show up right. -exclude takes a string[] argument, so:
gci -recurse -filter *.bak -exclude *.*.bak,windows |? {!$_.psiscontainer}