How to exclude sub folders - powershell

I want to get all "*.exe" files from one folder. It has 3 sub folders. but I want get files from 2 sub folders only. I am trying to use -Exclude in PowerShell. It is working to exclude single file but it is not working to exclude a folder. can anybody tell how to solve it.
This is what I am using the below code it is working to exclude "HWEMNGR.EXE" etc files but Its not allowing me to exclude a sub folder from main folder.
$Files = Get-ChildItem -Path "D:\depot\main" -Recurse -Include "*.exe" -Exclude HWEMNGR.EXE,wmboot.exe,SDClientMobileEdition.exe | % { $_.FullName }
Thanks

-Exclude is good for file names however it does not have a good track record for folder name\path exclusion. You should use a Where-Object clause to address that.
$Files = Get-ChildItem -Path "D:\depot\main" -Recurse -Include "*.exe" -Exclude "HWEMNGR.EXE","wmboot.exe","SDClientMobileEdition.exe" |
Select-Object -ExpandProperty FullName |
Where-Object{$_ -notmatch "\\FolderName\\"}
The snippet % { $_.FullName } was replaced by Select-Object -ExpandProperty FullName which does the same thing. Then we use Where-Object to exclude paths where FolderName is not there. It is regex based so we double up on the slashes. This also helps make sure we exclude folders and not a file that might be called "FolderName.exe"
Alternate approach
Like TheMadTechnician points out you could come at this from another direction and just ensure the files come from the only two folders you really care about. Get-ChildItem will take an array for paths so you could also use something like this.
$paths = "D:\depot\main\install","D:\depot\main\debug"
$excludes = "HWEMNGR.EXE","wmboot.exe","SDClientMobileEdition.exe"
$Files = Get-ChildItem $paths -Filter "*.exe" -Exclude $excludes | Select-Object -ExpandProperty FullName
$paths is the only two folders you want results from. You still exclude the files you dont want and then just return the full file paths.

I will take the following example to explain how I do it:
Get-ChildItem C:\a -include "*.txt" -recurse | %{$_.fullname}
C:\a\b\b1.txt
C:\a\b\b2.txt
C:\a\b\b3.txt
C:\a\c\c1.txt
C:\a\c\c2.txt
C:\a\c\c3.txt
C:\a\d\d1.txt
C:\a\d\d2.txt
Here under C:\a, the subfolders are b,c and d
Now I want *.txt files from b and c to be listed and I want to exclude any file under d subfolder.
To achieve this I introduce a where (also used as "?") condition as follows
Get-ChildItem C:\a -include "*.txt" -recurse | ?{$_.fullname -notlike "C:\a\d\*"} | %{$_.fullname}
C:\a\b\b1.txt
C:\a\b\b2.txt
C:\a\b\b3.txt
C:\a\c\c1.txt
C:\a\c\c2.txt
C:\a\c\c3.txt
I hope this solves your problem.

Related

Renaming all the folder names in the current dir that has files and sub directories inside using Powershell

I have a set of folders like below, which has files and subfolders inside them, i want to rename
all the smv2103* folders to smv2106* folders , keeping the files and subfolders inside them intact using Powershell
E:\online\smv2103pdf
E:\online\smv2103mac
E:\online\smv2103frp
E:\online\smv2103rep
E:\online\smv2103soc
E:\online\smv2103bid
E:\online\smv2103rem
E:\online\smv2103nop
E:\online\smv2103gac
E:\online\smv2103pam
Any help would be highly appreciated , as Rename-Item itself is not working because of the files and subfolders inside
Get-ChildItem "E:\online\*" -Directory|
Where{ ( $_.Name -like "*2103*") } |
ForEach-Object{
((Get-Content $_.FullName -Raw) -replace "2103","2106") | Set-Content -Path $_.FullName
}
So you just want to rename the folders? You use Get-Content to get the content from within files. To filter for a certain criteria, Get-ChildItem has a -Filter Parameter you can use.
Get-ChildItem -Filter "*2013*"
When filtering in powershell, its good practice filtering as far left as possible. Now, knowing your just looking to rename the folders themselves, we can remove Get-Content and pipe the info onto Rename-Item.
Get-ChildItem -Path "E:\online*" -Filter "*2013*" -Directory |
ForEach-Object {
Rename-Item -Path $_.FullName -NewName $_.Name.Replace('2013','2016')
}

Using a PowerShell script, is there a way to get a list of Items from a folder that excludes items that have a duplicate BaseName?

I want to store items in a var from a directory with duplicate names, but different extensions. For example: there are files with .jpg and files with .webp with the same BaseName. I would like to excludes those and only add the ones that don't have a similar .webp twin.
Code I used to get all the files:
$images = Get-ChildItem $dir
I would like to store all the files that don't have a .webp twin with the same BaseName in $images.
Use the Group-Object cmdlet:
$images = Get-ChildItem -File $dir\*.jpg, $dir\*.webp |
Group-Object BaseName |
Where-Object { $_.Group.Extension -notcontains '.webp' } |
ForEach-Object Group
Note: If recursive file retrieval were to be used (-Recurse), you could simplify to
Get-ChildItem -Recurse -File $dir -Include *.jpg, *.webp; perhaps surprisingly, -Include is only properly supported with -Recurse - see GitHub issue #3304.

How to find all folders which has folder named "Intro" AND filetype *.mp4

The closest I got was using powershell Get-ChildItem given multiple -Filters
Get-ChildItem -Include "Intro", "*.mp4" -Recurse
I think, -Include with multiple params work as OR operator. It gives folders with either "Intro" folder OR "*.mp4" files.
But I need AND condition. Folder must contain a folder named "Intro" and "*.mp4" files.
I need folders structured following -
E:.
└───test1.mp4
└───test2.mp4
└───test3.mp4
└───test4.mp4
└───test5.mp4
└───test6.mp4
└───Intro
Update 1
I am searching for folders which meet two condition.
It must have a subfolder named Intro AND
It must have *.mp4 files.
The answer would look something like the following I guess.
Get-ChildItem -Directory -Recurse {HasSubFolderNamedIntro && HasMP4Files}
Just wanted to add another Method (one-liner) to get the folder which contains mp4 files and a folder Intro (probs to #HenrikStanleyMortensen for most of it):
(Get-ChildItem -include "*.mp4" -Recurse -File).DirectoryName | Select-Object -Unique | Where-Object {(Get-ChildItem $_ -Recurse -Include 'Intro' -Directory)}
Do you need the command to only return the file objects or do you also want the folder objects?
If just the files I would do it like this:
Get-ChildItem -include "*.mp4" -Recurse -File | Where-Object {$_.Directory.Name -match 'Intro'}
So we use the include to find the mp4 files and reduce the amount of objects we pipe.
Then we pipe it to Where-Object and look for the property with the name of the folder and says we want it to contain the word "intro". If the folder needs to be called Intro exactly and not just contain it you can change the -match to -eq
Edit
To get the directories then we could do it like this:
(Get-ChildItem -include "*.mp4" -Recurse | Where-Object {$_.Directory.Name -match 'Intro'}).DirectoryName | Select-Object -Unique
Now we say that all the files that we found that matches our search, we want to see the full directory path of those files.
To only get one match per directory, so if we have 1 directory that matches with multiple mp4 files, and we don't want to see that same directory in our output one time per file, we can pipe the result into Select-Object -Uniqueto only see each directory once.
Edit 2
After clarification from OP.
To find a folder that contains both mp4 files and a subfolder called intro I don't think we can do that only from the Get-ChildItem command in any way I know of, so we can loop through each folder like this:
$Files = (Get-ChildItem -include "*.mp4" -Recurse -File).DirectoryName | Select-Object -Unique
foreach($File in $Files) {
Get-ChildItem -Path $File.DirectoryName -Recurse -Include 'Intro' -Directory
}
We Pipe to the Select-Object -Unique to make sure that folders with multiple mp4 files are not looped through more than once thus giving us an output with the same intro folder multiple times.

XCOPY deployment script - how to include certain files?

I need to copy only certain parts of a folder using Powershell, specifically this list:
$files = #("MyProgram.exe",
"MyProgram.exe.config",
"MyProgram.pdb",
".\XmlConfig\*.xml")
In human readable form: 3 specific MyProgram.* files under root of target folder and all XML files under XmlConfig folder which itself is under root of source path (..\bin\Release\ in my case). XmlConfig folder must be created in destination, if it does not exist.
What I have tried:
(1) I tried the following, but it did not work, i.e. no folder or files were created at the destination path:
Copy-Item -Recurse -Path "..\bin\Release\" -Destination ".\Test\" -Include $files
(2) When -Include is removed, whole folder structure is successfully created, including subfolders and files:
Copy-Item -Recurse -Path "..\bin\Release\" -Destination ".\Test\"
It must be something wrong with my understanding of how -Include filter works:
(3) I tested an assumption that -Include needs an array of wildcards, but this did not work either:
$files = #("*MyProgram.exe*",
"*MyProgram.exe.config*",
"*MyProgram.pdb*",
"*.\XmlConfig\*.xml*")
Please advise on how to properly do Copy-Item in my case.
UPDATE (based on below answers):
I am looking for a generic implementation that takes an array of strings. It opens the possibility to put all necessary files/paths in one place, for easy editing, so that a non-Powershell knowledgeable person can understand and modify it as required. So in the end it would be single script to perform XCOPY deployments for any project, with input file being the only variable part. For above example, the input would look like this (saved as input.txt and passed as an argument to the main script):
MyProgram.exe
MyProgram.exe.config
MyProgram.pdb
.\XmlConfig\*.xml
I would prefer wildcards approach, since not many people know regex.
i don't know what is wrong with filter but you can still do
$files | % { copy-item ..\bin\release\$_ -Destination .\test}
if you want to preserve directoty structure you'll have to weak this a little, like :
$sourcedir="c:\temp\test"
$f=#("existing.txt","hf.csv";"..\dir2\*.txt")
$f |%{
$source=ls (join-Path $sourcedir $_) |select -expand directoryname
if ("$source" -like "$sourcedir*"){
$destination=$source.Substring($sourcedir.Length)+".\"
}
else{
$destination=$_
}
copy-item $sourcedir\$_ -Destination $destination -WhatIf
}
AFAICT -Include works only with file names or directory names and not combinations i.e. paths. You can try something like this:
$files = 'MyProgram\.exe|MyProgram\.exe\.config|MyProgram\.pdb|XmlConfig\\.*?\.xml'
Get-ChildItem ..\bin\release -r | Where {!$_.PSIsContainer -and ($_.FullName -match $files)} |
Copy-Item -Dest .\test
With wildcards you could do it this way:
$files = #('*MyProgram.exe','*MyProgram.exe.config','*MyProgram.pdb','*\XmkConfig\*.xml')
Get-ChildItem ..\bin\release -r |
Foreach {$fn=$_.Fullname;$_} |
Where {!$_.PSIsContainer -and ($files | Where {$fn -like $_})} |
Copy-Item -Dest .\test

How to use wildcards with directories in PowerShell's Get-ChildItem -Exclude cmdlet

For a simple example, let's say I have a folder, Root, with three folders in it; Folder1, Folder2, and Folder3. Each of these folders (including Root) has a bunch of files in them, including .pdb files. I want to use the PowerShell Get-ChildItem cmdlet to return all of the files in all of the folders (including Root), except for the .pdb files in Folder2. If I use:
Get-ChildItem -Path C:\Root -Recurse -Exclude *.pdb
Then I get back all of the non-.pdb files in all of the directories, which is close to what I want. So I assumed that the following would achieve what I want:
Get-ChildItem -Path C:\Root -Recurse -Exclude \*\\Folder2\\*.pdb
But this does not exclude any of the pdb files in Folder2 (or any other folders). I have tried several variants for the -Exclude filter, such as Folder2\\\*.pdb, but I cannot get it to work. In fact, even using \*\\\*.pdb does not seem to do anything; no .pdb files get excluded from any folders.
So it seems that the wildcards cannot be used for directories, only filenames, but I assume I am just doing something wrong. I found this article explaining the wildcard and range operators, but unfortunately it does not discuss using them with directory names; only file names.
I haven't seen the exclude parameter working with directories either.
You could try piping into Where-Object. That is,
Get-ChildItem -Recurse *.pdb | Where-Object {$_.FullName -notMatch "folder2"}
OhadH's answer is almost there.
You can use
Get-ChildItem -Recurse | Where-Object {$_.FullName -notmatch "folder2" -or $_.Name -notlike "*.pdb" }
or
Get-ChildItem -Recurse | ? {$_.FullName -notlike "*folder2*" -or $_.Name -notlike "*.pdb" }
It depends on where your folder2 is located. But I think you got the idea.
I hate to say it, but PowerShell is not any convenient as Bash or other shells.
gci "C:\Root" -Recurse | Where-Object {$_.FullName -notlike "*Folder2\*.pdb*"} | Export-CSV "C:\Root\Export.csv" -NoType
Tried, tested & one liner :-). This works as I have copied your folder & file structure to replicate. Sorry about it being a few years late, however.
Feel free to tweak the code to your needs, obviously.
Use asterisk and recursion parameter:
Get-ChildItem -Path "C:\Users\*\AppData\Roaming\Microsoft\"
-Filter "*config*" -Recurse | Select -ExpandProperty FullName
This should be work for you:
Get-ChildItem -Path C:\Root -Exclude "*\Folder2\*.pdb" -Recurse