Exclude parameter with asterisks in Get-Childitem - powershell

I want to use Get-ChildItem Powershell method and pass a list to -Exclude parameter, but I want to use wildcards (like *$exclude* with single string) so that all files that only contain any of the excludes will be excluded. How can I do it?
I want to exclude from Get-ChildItem method return all files that only contain any of the excludes in their names.

I used Contains with a ! (not). You can also use Regex. I also used ALL to get more than one match string. I made the list capital letters and then used ToUpper() to get both lower and uppercase to match. See code below
$excludeList = #("LOG", "CSV")
Get-ChildItem -Path "c:\temp\" `
| where {![Linq.Enumerable]::Any([string[]]$excludeList, [Func[string,bool]]{ param($excludeItem); return $_.Name.ToUpper().Contains($excludeItem) }) -eq $True} `
| ForEach-Object {Write-Host $_.Name}
Using Regex
$excludeList = #("LOG", "CSV")
Get-ChildItem -Path "c:\temp\" `
| where {![Linq.Enumerable]::Any([string[]]$excludeList, [Func[string,bool]]{ param($excludeItem); return $_.Name.ToUpper() -match $excludeItem }) -eq $True} `
| ForEach-Object {Write-Host $_.Name}

Related

remove items, except folders in an array

So, i've been scratching my head for a while now and can't seem to figure it out.
I want to delete files and folders older than 'x' days <-- this works fine
I want to delete empty directories left behind <-- this works fine as well
I also want to have some exceptions: filenames and foldernames. The filename exception works fine, but folders don't. There is something strange though. If i put only 1 name in the array of folders i don't want to delete, it works just fine. But if i put multiple in, it suddenly doesn't work anymore?
I have the idea it might be something simple i'm completely missing
$limit = (Get-Date).AddDays(-120)
$path = "C:\Users\user\Documents\files"
$ExcludedFileNames = #("*file1*", "*file2*")
$ExcludedFolders = #("*folder1*", "*folder2*")
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force -exclude $ExcludedFileNames |
Where-Object {($_.FullName -notlike $ExcludedFolders) -and (!$_.PSIsContainer) -and ($_.LastWriteTime -lt $limit) } |
Remove-Item -Force
# Delete any empty directories left behind after deleting the old files.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse
Instead of $.FullName i tried $.Name
Instead of -notlike i tried -notin
I also tried removing the array and put the variables after where-object
I also tried to copy other code from lots of posts but didn't seem to help.
The problem is that -notlike expects a single string as it's right-hand side operand, and so the $ExcludedFolders variable is coerced into the stringvalue "*folder1* *folder2*".
The comparison 'C:\some\path\to\a\folder1\with\a\file.exe' -notlike '*folder1* *folder2*' obviously fails.
You can solve this by using the -notmatch regex operator instead:
$ExcludedFolders = #('folder1', 'folder2') # note that we no longer need the wildcards
# later
... |Where-Object {$_.FullName -notmatch ($ExcludedFolders.ForEach{[regex]::Escape($_)} -join '|') -and (-not $_.PsIsContainer) -and $_.LastWriteTime -lt $limit}
The | is the alternation operator in regex, effectively functioning as an OR
I would use wildcards on the file names to use with the -Exclude parameter, and create a regex string for the foldernames to exclude you can use in the Where-Object clause.
Something like this:
$limit = (Get-Date).AddDays(-120).Date # set to midnight instead of the current time
$path = 'C:\Users\user\Documents\files'
$ExcludedFileNames = '*file1*', '*file2*' # wildcards for the Exclude parameter
$ExcludedFolders = 'folder1','folder2' # can be a partial name, do not use wildcards here
# create a regex string for the folder names to exclude
# each item will be Regex Escaped and joined together with the OR symbol '|'
$FoldersToSkip = ($ExcludedFolders | ForEach-Object { [Regex]::Escape($_) }) -join '|'
# Delete files older than the $limit.
Get-ChildItem -Path $path -File -Recurse -Force -Exclude $ExcludedFileNames |
Where-Object {($_.DirectoryName -notmatch $FoldersToSkip) -and ($_.LastWriteTime -lt $limit) } |
Remove-Item -Force
# Delete any empty directories left behind after deleting the old files.
(Get-ChildItem -Path $path -Recurse -Directory -Force).FullName |
Where-Object { !( Get-ChildItem -Path $_ | Select-Object -First 1 ) } |
Sort-Object -Property Length -Descending |
Remove-Item -Force

Excluding multiple items from Get-ChildItem using Powershell version 4

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*_' }

Advance file search with powershell

I'm fairly new to Powershell and programming in general. I want to search files using Powershell having multiple conditions. I have managed to write this code
$Drives = Get-PSDrive -PSProvider 'FileSystem'
$Filename= 'Result'
$IncludeExt= '*csv,*docx'
$StartDate= '11/1/20'
$EndDate= '1/26/21'
Get-ChildItem -Path $Drives.Root -Recurse |Where-Object {$IncludeExt -match $_.Extension} | Where-Object { $_.BaseName -match $Filename} | Where-Object {$_.lastwritetime -ge $StartDate -AND $_.lastwritetime -le $EndDate} |
foreach{
$Item = $_.Basename
$Path = $_.FullName
$Type = $_.Extension
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="filePath";e={$Path}},`
#{n="Folder/File";e={if($Folder){"Folder"}else{$Type}}}`
}| Export-Csv D:\FFNew.csv -NoTypeInformation
This works well when the all variables are mentioned. But how do I get this to work when
Case1: If $Filename is empty then it gives all the files with the mentioned extensions and files modified in Range of dates
Case2: If $IncludeExt is left empty then it gives all files with the $Filename mentioned, currently it gives only the folders and files modified in Range of dates
Case 3: If $Filename and $IncludeExt is left empty it gives all the files modified between the $StartDate and $EndDate
Pranay,
[EDITED]
Ok, here's the revised (exact) script with notes and sample output. Note: you'll have to change the items that are specific to my machine!
$Drives = Get-PSDrive -PSProvider 'FileSystem'
$Filename = "*" #for all or "*partial name*"
$IncludeExt = $Null #for no ext. or "*.csv","*.docx",etc...
$StartDate = '01/1/2020' #to ignore this use 1/1/1920
#For latest date use below otherwise specify date.
$EndDate = (Get-Date).ToShortDateString()
#Note: below uses only 3rd drive in the array remove [2] for all.
$GCIArgs = #{Path = $Drives[2].Root
Recurse = $True
}
If ($Null -ne $IncludeExt) {
$GCIArgs.Add("Include",$IncludeExt)
}
Get-ChildItem #GCIArgs |
Where-Object {($_.BaseName -Like $Filename) -and
($_.lastwritetime -ge $StartDate) -and
($_.lastwritetime -le $EndDate) } |
foreach{
$Item = $_.Basename
$Path = $_.FullName
$Type = $_.Extension
$Type = & {if($_.PSIsContainer){"Folder"}else{$_.Extension}}
$Age = $_.CreationTime
$Path | Select-Object #{n="Name" ;e={$Item}},
#{n="Created" ;e={$Age}} ,
#{n="filePath" ;e={$Path}},
#{n="Folder/File";e={$Type}}
} | Export-Csv -LiteralPath 'G:\BEKDocs\FFNew.csv' -NoTypeInformation
Notes:
$IncludeExt is specified as $Null if it is not used and if used the list is like this ".csv",".docx"
$Filename is specified as "*" for all filenames. Also changed the test from -match to -like so partial filenames should include *, e.g. "partial name".
Notice I changed the location of the check for Extensions to use the -Include parameter of the Get-ChildItem vs checking in the Where-Object.
Changed the piping of data to successive Where-Object clauses and replaced with -and operator, same effect and more efficient.
Changed the test for Directories to use the PSIsContainer property, couldn't see where you were getting the value for $Folder.
Removed the continuation characters from the Select-Object as the comma serves that purpose and is cleaner.
Sample output on Single drive (per code shown above) with some lines hidden for space considerations but notice the last line number.
Sample output on all drives (code edited as per comment in code), again lines hidden for space but showing multiple drives and final line number.
HTH

How to copy files in powershell that contain variable in name?

I am trying to copy files that match an environment identifier set to a variable. The output of the command looks good until in include the where-object section. I only want to copy files that contain the string in the $environmenttype string.
What do I need to change to get the where-object to operate correctly?
$environmenttype = "dev"
Write-Output "environmenttype is set to $environmenttype"
Get-ChildItem -path "\content" | Select-Object -ExpandProperty name | Where-Object {$_ -contains "$environmenttype"} | Copy-Item -Path "C:\newdir"
How I'd write it:
Get-ChildItem "\content" -File | where BaseName -like "*$environmenttype*" | Copy-Item "C:\newdir"
I am using the simplified version of Where-Object and the -like operator/parameter and a wildcard pattern (note the asterisks *).
BaseName is the name of the file without extension.
I omitted some implicit parameter names, and added the -File switch to include files only.
(Note that the path in Copy-Item is set by the pipeline, so the parameter is actually -Destination)
You can use match predicate, which supports variable usage.
$filter = "ansi"
Get-ChildItem -path "c:\dev\" | Where-Object {$_.Name -match "$filter"} | Copy-Item -Destination C:\dev\testFolder

Correction in sub folder names by replacing first two characters, if needed

I am using below Powershell script which successfully traverses through all my case folders within the main folder named Test. What it is incapable of doing is to rename each sub folder, if required, as can be seen in current and desired output. Script should first sort the sub folders based on current numbering and then give them proper serial numbers as folder name prefix by replacing undesired serial numbers.
I have hundreds of such cases and their sub folders which need to be renamed properly.
The below output shows two folders named "352" and "451" (take them as order IDs for now) and each of these folders have some sub-folders with a 2 digit prefix in their names. But as you can notice they are not properly serialized.
$Search = Get-ChildItem -Path "C:\Users\User\Desktop\test" -Filter "??-*" -Recurse -Directory | Select-Object -ExpandProperty FullName
$Search | Set-Content -Path 'C:\Users\User\Desktop\result.txt'
Below is my current output:
C:\Users\User\Desktop\test\Case-352\02-Proceedings
C:\Users\User\Desktop\test\Case-352\09-Corporate
C:\Users\User\Desktop\test\Case-352\18-Notices
C:\Users\User\Desktop\test\Case-451\01-Contract
C:\Users\User\Desktop\test\Case-451\03-Application
C:\Users\User\Desktop\test\Case-451\09-Case Study
C:\Users\User\Desktop\test\Case-451\14-Violations
C:\Users\User\Desktop\test\Case-451\21-Verdict
My desired output is as follows:
C:\Users\User\Desktop\test\Case-352\01-Proceedings
C:\Users\User\Desktop\test\Case-352\02-Corporate
C:\Users\User\Desktop\test\Case-352\03-Notices
C:\Users\User\Desktop\test\Case-451\01-Contract
C:\Users\User\Desktop\test\Case-451\02-Application
C:\Users\User\Desktop\test\Case-451\03-Case Study
C:\Users\User\Desktop\test\Case-451\04-Violations
C:\Users\User\Desktop\test\Case-451\05-Verdict
Thank you so much. If my desired functionality can be extended to this script, it will be of great help.
Syed
You can do the following based on what you have posted:
$CurrentParent = $null
$Search = Get-ChildItem -Path "C:\Users\User\Desktop\test" -Filter '??-*' -Recurse -Directory | Where Name -match '^\d\d-\D' | Foreach-Object {
if ($_.Parent.Name -eq $CurrentParent) {
$Increment++
} else {
$CurrentParent = $_.Parent.Name
$Increment = 1
}
$CurrentNumber = "{0:d2}" -f $Increment
Join-Path $_.Parent.FullName ($_.Name -replace '^\d\d',$CurrentNumber)
}
$Search | Set-Content -Path 'C:\Users\User\Desktop\result.txt'
I added Where to filter more granularly beyond what -Filter allows.
-match and -replace both use regex to perform the matching. \d is a digit. \D is a non-digit. ^ matches the position at the beginning of the string.
The string format operator -f is used to maintain the 2-digit requirement. If you happen to reach 3-digit numbers, then 3 digit numbers will be output instead.
You can take this further to perform a rename operation:
$CurrentParent = $null
Get-ChildItem . -Filter '??-*' -Recurse -Directory | Where Name -match '^\d\d-\D' | Foreach-Object {
if ($_.Parent.Name -eq $CurrentParent) {
$Increment++
} else {
$CurrentParent = $_.Parent.Name
$Increment = 1
}
$CurrentNumber = "{0:d2}" -f $Increment
$NewName = $_.Name -replace '^\d\d',$CurrentNumber
$_ | Where Name -ne $NewName | Rename-Item -NewName $NewName -WhatIf
}
$NewName is used to simply check if the new name already exists. If it does, a rename will not happen for that object. Remove the -WhatIf if you are happy with the results.