3

Recursive -notmatch in get-childitem with regex

I am trying to find some files with set extensions with the following code.

$include = @('*.keystore', '*.cer', '*crt', '*pfx', '*jks', '*.ks')
$exclude = [RegEx]'^C:\\Windows|^C:\\Program Files|^C:\\Documents and Settings|^C:\\Users|\bBackup\b|\bbackup\b|\brelease\b|\breleases\b|\bReleases\b|\bRelease\b'
$trace="trace.txt"
Get-ChildItem "D:\","C:\" -Directory |
  where FullName -notmatch $exclude | ForEach {
  Get-ChildItem -Path $_.FullName -Include $include -Recurse -Force -EA 0| 
  Select-Object -ExpandProperty FullName 
} *>&1 >> $trace

It works but only excludes if the root directory contains one of the patterns. Ex. Excludes D:\Backup but includes D:\something\Backup

Is there a way to exclude the second folder as well?

Delyan
  • 75
  • 1
  • 7
  • Maybe *wildcard expressions* are better suited for this? – Abraham Zinala Jan 29 '22 at 01:15
  • 1
    I don't see a problem with your regex; `'D:\something\Backup' -match $exclude` returns `$true`, as intended. – mklement0 Jan 29 '22 at 02:10
  • 1
    Also, if you construct a `[regex]` instance explicitly - instead of passing a _string_ to `-notmatch` - you're creating a case-_sensitive_ regex instance. By contrast, using a string implicitly creates a case-_insensitive_ instance, in line with PowerShell's fundamentally case-insensitive behavior; e.g. `'foo' -match 'FOO'` returns `$true`. – mklement0 Jan 29 '22 at 02:15
  • @mklement0 I have D:\something\backup\test.jks. The above does not exclude it. However, excludes D:\backup. Test it by creating these 2 folders. – Delyan Jan 29 '22 at 09:57
  • 1
    You need to add `-Recurse` also to the _first_ `Get-ChildItem` call, so that folders such as `backup` are excluded on every level of the directory hierarchy. Theo's solution is simpler and more efficient, however. – mklement0 Jan 29 '22 at 22:43

1 Answers1

4

Try this with a single call to Get-ChildItem and have it look for files. Then in the Where-Objectclause, you filter on the DirectoryName to exclude the folders you don't want in the output.

Also, you can simplify your regex and use it case-insensitively.

Try:

$include = '*.keystore', '*.cer', '*.crt', '*.pfx', '*.jks', '*.ks'
$exclude = '^C:\\(Windows|Program Files|Documents and Settings|Users)|\bBackup\b|\breleases?\b'
$trace   = 'trace.txt'
Get-ChildItem -Path 'C:\','D:\' -File -Include $include -Recurse -Force -ErrorAction SilentlyContinue | 
  Where-Object { $_.DirectoryName -notmatch $exclude } |
  Select-Object -ExpandProperty FullName |
  Set-Content -Path $trace -PassThru

Switch -PassThru makes the result also display on screen


The C:\ drive contains system-defined junction points you do not have access to, even when running as admin.
Based on this answer, you can change the code to:

$include = '*.keystore', '*.cer', '*.crt', '*.pfx', '*.jks', '*.ks'
$exclude = '^C:\\(Windows|Program Files|Documents and Settings|Users)|\bBackup\b|\breleases?\b'
$trace   = 'trace.txt'
Get-ChildItem -Path 'C:\','D:\' -File -Include $include -Force -Recurse -Attributes !Hidden, !System, !ReparsePoint -ErrorAction SilentlyContinue | 
  Where-Object { $_.DirectoryName -notmatch $exclude } |
  Select-Object -ExpandProperty FullName |
  Set-Content -Path $trace -PassThru

As per your comment, here's an example on how you can do this on remote computers, and save the resulting file locally on your computer.

For this demo, I have removed the -Force switch because as you stated you don't need to capture hidden or system files.

$remoteComputers = 'PC01', 'PC01'  # your list of remote machine names here
$trace   = 'trace.csv'             # make your output a CSV file, so you can have the computernam in there as well

$result = Invoke-Command -ComputerName $remoteComputers -ScriptBlock {
    $include = '*.keystore', '*.cer', '*.crt', '*.pfx', '*.jks', '*.ks'
    $exclude = '^C:\\(Windows|Program Files|Documents and Settings|Users)|\bBackup\b|\breleases?\b'
    Get-ChildItem -Path 'C:\','D:\' -File -Include $include -Recurse -ErrorAction SilentlyContinue | 
          Where-Object { $_.DirectoryName -notmatch $exclude } |
          # this time, output an object with the current computername and the file fullname combined
          Select-Object @{Name = 'ComputerName'; Expression = {$env:COMPUTERNAME}}, FullName
}

# save the resulting array of objects in a single CSV file on YOUR local computer
$result | Set-Content -Path $trace -PassThru

You may need to append parameter -Credential to Invoke-Command and feed it admin credentials for the remote machines

Theo
  • 57,719
  • 8
  • 24
  • 41
  • Thanks, it is working but not with C:\ for some reason it gives Get-ChildItem : Access is denied even with -EA0 and the admin privileges. – Delyan Jan 29 '22 at 14:17
  • @Delyan Please see my edit, where I have added code to to hopefully prevent Access Denied on the C:\ drive – Theo Jan 29 '22 at 14:36
  • thank you. The issue was with an installed software and one of its folders. Removed -force and now I dont get the error. Probably it wont be a problem. – Delyan Jan 29 '22 at 14:45
  • 1
    @Delyan No, no problem. Removing `-Force` will work, but then it will skip all hidden and/or system files (don't think you are after those, so it shoud be fine) – Theo Jan 29 '22 at 14:46
  • any idea why this does not work on remote servers? The $trace does not get populated. If I remove the filter and list disks for example it works. – Delyan Jan 30 '22 at 13:16
  • @Delyan Please see my edit – Theo Jan 31 '22 at 12:03