Novice scripter here and I have a script that connects to multiple file servers and recurses through directories on them looking for files over 90 days old. All that works great.
I'm using Get-ChildItem -include and -exclude to filter the files I want to report on but I also need to filter out certain directories and get-childitem can't filter directories so I'm piping the results over to a Where-Object that then compares the $_.FullName property to strings I want to exclude.
My problem is that I'm having to adjust the directory names I'm filtering for on a regular basis depending on what clients name their files and managing the script is getting a little out of hand as I have to keep adding -and ($_.FullName -notMatch "BACKUPS") conditions to the get-childitem line.
Here's my relevant code:
$Exclusions = ('*M.vbk','*W.vbk','*Y.vbk')
$Files = Get-ChildItem $TargetFolder -include ('*.vib','*.vbk','*.vbm','*.vrb') -Exclude $Exclusions -Recurse -File |
Where {($_.LastWriteTime -le $LastWrite) -and
($_.FullName -notMatch "BACKUPS") -and
($_.FullName -notMatch "Justin") -and
($_.FullName -notMatch "Monthly") -and
($_.FullName -notMatch "Template") -and
($_.FullName -notMatch "JIMMY") -and
($_.FullName -notMatch "ISAAC")}
I set up the $Exclusions variable to use in Get-ChildItem so that I have a single variable to adjust as needed. Is there a way to condense all the individual Where ($.FullName -notMatch "JIMMY") entries to just use one variable like... Where ($.FullName -notMatch $DirectoryListVariable)?
Basically I just need to make this easier to manage and change if that's possible. If not then I can just keep adding new lines but I'm hoping there is a better way.
Thanks for your time!
Use regex alternation (|) to match any one of multiple patterns:
# Array of name substrings to exclude.
$namesToExclude = 'BACKUPS', 'Monthly', 'Justin', 'Template', 'JIMMY', 'ISAAC' # , ...
# ...
... | Where-Object {
$_.LastWriteTime -le $LastWrite -and
$_.FullName -notmatch ($namesToExclude -join '|')
}
For case-sensitive matching, use -cnotmatch
As an alternative to defining an array of patterns to then -join with | to form a single string ('BACKUPS|Monthly|...'), you may choose to define your names as part of such a single string to begin with.
Do note that the resulting string must be a valid regex; that is, the individual names are interpreted as regexes (subexpressions) too.
If that is undesired (note that it isn't a problem here, because the specific sample names are also treated literally as regexes), you can escape them with [regex]::Escape().
Finally, note that -match and its variant perform substring (subexpression) matching; to match input strings in full, you need to anchor the expressions, namely with ^ (start of the string) and $ (end of the string); e.g.
'food' -match 'oo' is $true, but 'food' -match '^oo$' is not ('oo' -match '^oo$' is).
Related
I am using the following code to get files using include & exclude criteria:
Get-ChildItem -Path Path/To/Folder -Include #("*Basic_TShirt_Men*",) -Exclude #("*black*","*purple*")
The above code filters files whose names contain Basic_TShirt_Men and doesn't contain black or purple. I need to modify the code to search by these criteria:
Criteria 1: Filename includes Basic_TShirt_Men and doesn't contain black or purple OR
Criteria 2: Filename includes Basic_Polo_Shirt and doesn't contain blue OR
Criteria 3: Filename includes Basic_Shirt_Women and doesn't contain pink
How to combine multiple Include/Exclude criteria like above?
Short answer is, you need an extra layer of filtering:
$include = #(
'*Basic_TShirt_Men*'
'*Basic_Polo_Shirt*'
'*Basic_Shirt_Women*'
)
Get-ChildItem Path\To\Folder\* -Include $include |
Where-Object {
$_.Name -like '*Basic_TShirt_Men*' -and $_.Name -notmatch 'black|purple' -or
$_.Name -like '*Basic_Polo_Shirt*' -and $_.Name -notlike '*blue*' -or
$_.Name -like '*Basic_Shirt_Women*' -and $_.Name -notlike '*pink*'
}
Note, the use of the trailing \* after the path should be there for -Include to work properly.
I'm iterating through a directory tree but trying to filter out a number of things.
This is my cobbled together code;
Get-ChildItem -Path $pathName -recurse -Filter index.aspx* -Exclude */stocklist/* | ? {$_.fullname -NotMatch "\\\s*_"} | Where {$_.FullName -notlike "*\assets\*" -or $_.FullName -notlike ".bk"}
Remove the name index.aspx from the returned item.
I want to filter out any file that starts with and underscore.
Exclude anything that contains /stocklist/ in its path.
Exclude anything that contains /assets/ in its path.
And exclude anything that contains .bk in its path.
This is working for everything but for the .bk in it's path. I'm pretty sure it's a syntax error on my part.
Thanks in advance.
You can create a regex string and use -notmatch on the file's .DirectoryName property in a Where-Object clause to exclude the files you don't need:
$excludes = '/stocklist/', '/assets/', '.bk'
# create a regex of the folders to exclude
# each folder will be Regex Escaped and joined together with the OR symbol '|'
$notThese = ($excludes | ForEach-Object { [Regex]::Escape($_) }) -join '|'
Get-ChildItem -Path $pathName -Filter 'index.aspx*' -File -Recurse |
Where-Object{ $_.DirectoryName -notmatch $notThese -and $_.Name -notmatch '^\s*_' }
I've got a bunch of files in the following format:
00092-100221-01M-V-3-20-001 Building A, plan 1M Piping
I'd like to rename only the files containing both the words "Building A" and "Piping"
I've tried the following code
dir -filter "*Building A*", "*Piping*" | Rename-Item -NewName {$_.basename + " As built" + $_.extension}
but I get an error stating that the parameter -filter does not support this method.
Is there a way I can filter for 2 conditions in Powershell?
The -Filter parameter accepts only one argument. You could use the -Include parameter but this can be tricky (look at the docs).
The easiest way to filter pipeline elements in general is the Where-Object cmdlet (short where):
dir | where { $_.Name -match "Building A" -and $_.Name -match "Piping" } | Rename-Item ...
Note: The -match operator uses regex matching, which is fine in this case. You could also use the -like operator with wildcards: $_.Name -like "*Piping*"
This is a follow up question of: PowerShell concatenate output of Get-ChildItem
This code works fine:
Get-ChildItem -Path "D:\Wim\TM1\TI processes" -Filter "*.vue" -Recurse -File |
Where-Object { $_.BaseName -match '^[0-9]+$' } |
ForEach-Object { ($_.FullName -split '\\')[-2,-1] -join '\' } |
Out-File D:\wim.txt
But I would need to restrict the search folder to only certain folders, basically, this filter: D:\Wim\TM1\TI processes\\*}vues (so all subfolders ending in }vues).
If I add that wildcard condition I get no result. Without the restriction, I get the correct result. Is this possible please?
The idea is to get rid of the 3rd line in the first output (which was a copy/paste by me) and also to minimize the number of folders to look at.
You can nest two Get-ChildItem calls:
An outer Get-ChildItem -Directory -Recurse call to filter directories of interest first,
an inner Get-ChildItem -File call that, for each directory found, examines and processes the files of interest.
Get-ChildItem -Path "D:\Wim\TM1\TI processes" -Filter "*}vues" -Recurse -Directory |
ForEach-Object {
Get-ChildItem -LiteralPath $_.FullName -Filter "*.vue" -File |
Where-Object { $_.BaseName -match '^[0-9]+$' } |
ForEach-Object { ($_.FullName -split '\\')[-2,-1] -join '\' }
} | Out-File D:\wim.txt
Note: The assumption is that all *.vue files of interest are located directly in each *}vues folder.
As for what you tried:
Given that you're limiting items being enumerated to files (-File), your directory-name wildcard pattern *}vues never gets to match any directory names and, in the absence of files matching that pattern, returns nothing.
Generally, with -Recurse it is conceptually cleaner not to append the wildcard pattern directly to the -Path argument, so as to better signal that the pattern will be matched in every directory in the subtree.
In your case you would have noticed your attempt to filter doubly, given that you're also using the -Filter parameter.
I have seen duplicate questions, but nothing worked for me.
I want to exclude looking into certain paths, while performing an operation. This doesn't work:
$archive = ("C:\Windows\Temp*","C:\Windows\winsxs*","C:\Windows\system*")
$final = Get-ChildItem C:\ -Include *.dll -Exclude $archive -Recurse | ? {
$_.PSIsContainer -and $_.FullName -notlike "\\obj\\?"
} | Where-Object {
$_.VersionInfo.LegalCopyright -notmatch 'Microsoft'
}
Please correct me where am I wrong? Do I have to use a foreach loop to iterate through items in $archive and exclude them individually? Pipeline or any other short command is there in this case?
First and foremost, a statement Get-ChildItem -Include *.dll will return only file objects (unless you have a folder named <something>.dll, which would be rather uncommon), so if you filter the output for directory objects ($_.PSIsContainer) you will obviously come up with an empty result. Since the rest of your code suggests that you want files anyway just remove the $_.PSIsContainer clause from your filter.
Also, the -Exclude parameter applies to the name of the items. You can't use it to exclude (partial) paths. If you want files from the given directories omitted from the result you should exclude them in your Where-Object filter with a regular expression match like this:
$_.FullName -notmatch '^C:\\windows\\(system|temp|winsxs)\\'
And finally, wildcard matches (-like, -notlike) require a * wildcard at beginning and/or end of the expression if you want to match a partial string:
PS> 'abcde' -like 'a*e'
True
PS> 'abcde' -like 'c*e'
False
PS> 'abcde' -like '*c*e'
True
Without the leading/trailing * the expression is automatically anchored at beginning/end of the string.
However, your pattern doesn't look like a wildcard expression in the first place. It looks more like a regular expression to me (to match paths containing \obj\ or ending with \obj). For that you'd also use the -notmatch operator:
$_.FullName -notmatch '\\obj\\'
From a perfomance perspective wildcard matches are more efficient, though, so it'd be better to use an expression like this:
$_.FullName -notlike '*\obj\*'
Making the trailing backslash optional is pointless, because Get-ChildItem returns a list of *.dll files, so none of the full paths will end with \obj.
Something like this should do what you want, assuming that I interpreted your code correctly:
$final = Get-ChildItem 'C:\' -Include '*.dll' -Recurse | Where-Object {
$_.FullName -notmatch '^C:\\windows\\(system|temp|winsxs)\\' -and
$_.FullName -notlike '*\obj\*' -and
$_.VersionInfo.LegalCopyright.Contains('Microsoft')
}