-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Find-ItemContent.ps1
246 lines (199 loc) · 11.8 KB
/
Find-ItemContent.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
function Find-ItemContent {
<#
.SYNOPSIS
Simple and fast function for finding any given string (regex pattern) in files on the filesystem (like grep on linux/unix)
.DESCRIPTION
Function that uses the EnumerateFiles method from the dotnet class System.Io.Directory to quickly find any file on the filesystem
and will then search for the given pattern in any found file using System.IO.StreamReader with System.Regex.
Class System.IO.EnumerationOptions does not exist in Powershell < 6 (so this function is not supported in the normal PowerShell, only in PowerShell Core/7)
.PARAMETER Path
Root path to search items for. Defaults to current working directory.
The relative or absolute path to the directory to search. This string is not case-sensitive.
.PARAMETER Pattern
string or regex pattern that will be used to find this pattern/string in the files found on the filesystem
.PARAMETER Name
(Default: '*')
This is the searchPattern for the Enumeration class.
The search string to match against the names of items in path.
This parameter can contain a combination of valid literal and wildcard characters,
but it doesn't support regular expressions.
You can use the * (asterisk) to match zero or more characters in that position.
You can also use the ? (question mark) to exactly match one character in that position.
Default is '*' = all items
One ore more strings to search for (f.e. '*.exe' OR '*.exe','*.log' OR 'foo*.log')
.PARAMETER Recurse
EnumerationOptions property RecurseSubdirectories. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER IgnoreInaccessible
EnumerationOptions property IgnoreInaccessible. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER MatchCasing
EnumerationOptions property MatchCasing. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER AttributesToSkip
EnumerationOptions property AttributesToSkip. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER MatchType
EnumerationOptions property MatchType. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER Depth
EnumerationOptions property Depth. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER IncludeSpecialDirectories
EnumerationOptions property ReturnSpecialDirectories. Check https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information.
.PARAMETER Options
RegexOptions. Check hhttps://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regexoptions?view=net-7.0 for more information.
.PARAMETER Highlight
Using Microsoft.PowerShell.Commands.MatchInfo class (Select-String) to pre filtered highlight output.
Direct usage of MatchInfo class is not possible since i also want to output the file where the pattern was found.
So we first use Regex class to find the pattern in a fast way and then use Select-String to output all as MatchInfo type to highlight the pattern.
.PARAMETER NotMatch
negates the matched pattern, so, show all results if pattern not matches.
Currently, pipelining is not supported (yet). Therefore we would need an InputObject parameter that accepts pipeline input and need to bypass the Enumeration of filesystem objects
.EXAMPLE
PS C:\> Find-ItemContent -Path c:\windows -Pattern 'WindowsUpdate' -Name '*.log' -Recurse
Using the alias psgrep. Search for pattern 'tinysvc' in all files in the current working directory recursively
.EXAMPLE
PS C:\> psgrep $pwd 'tinysvc' '*' -Recurse
Search for pattern 'WindowsUpdate' in all .log files in c:\windows directory recursively
.EXAMPLE
PS C:\> psgrep 'test'
Shortest possible command line call. Searching for 'test' in (-Path) the current directory and -Name will be '*' (all files in current directory)
.EXAMPLE
PS C:\> psgrep 'test' -H
Same as above example but the pattern 'test' will be highlightet (-H/-Highlight) in the output
.EXAMPLE
PS C:\> psgrep 'measure' -H -O IgnoreCase
Same as above (only with pattern 'measure') but it ignores casing (so it is not CaseSensitive). -O is the short version of -Options and -Options is an alias of -RegexOptions
.EXAMPLE
PS C:\> psgrep 'measure' -H -R -O IgnoreCase
Equivalent to linux/unix grep: grep -HiR 'measure'
.EXAMPLE
PS C:\> psgrep 'output' -Name 'CHANGELOG.md'
Searches for pattern 'output' in file 'CHANGELOG.md' in current directory
.EXAMPLE
PS C:\> psgrep 'output' -Name 'CHANGELOG.md' -Not
Negates above search (grep -v). Searches for pattern 'output' in file 'CHANGELOG.md' in current directory and outputs all lines that are not match to this pattern.
.LINK
https://github.com/eizedev/PSItems
.LINK
https://docs.microsoft.com/en-us/dotnet/api/system.io.directoryinfo?view=net-7.0
.LINK
https://docs.microsoft.com/en-us/dotnet/api/system.io.enumerationoptions?view=net-7.0
.LINK
https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regexoptions?view=net-7.0
.NOTES
Author: Eizedev
Last Modified: Jan 01, 2023
Version: 1.3
#>
#Requires -PSEdition Core
[CmdletBinding()]
[OutputType('System.String')]
param (
# Pattern for searching for in files
[Parameter(Mandatory = $true, Position = 0)]
[string]
$Pattern,
# Path to search for files
[Parameter(Mandatory = $false, Position = 1)]
[string]
$Path = $pwd,
# Name for searching for files
[Parameter(Mandatory = $false, Position = 2)]
[string[]]
$Name = '*',
# Include subdirectories if given
[Parameter(Mandatory = $false)]
[switch]
$Recurse,
# Sets a value that indicates whether to skip files or directories when access is denied (for example, UnauthorizedAccessException or SecurityException). Default is true
[Parameter(Mandatory = $false)]
[bool]
$IgnoreInaccessible = $true,
# Match case if given
[Parameter(Mandatory = $false)]
[string]
[ValidateSet('PlatformDefault', 'CaseSensitive', 'CaseInsensitive')]
$MatchCasing = 'PlatformDefault',
# Attributes of files to skip (not to search for) (Defaults to FileAttributes.Hidden | FileAttributes.System). Specify 0 to disable
[Parameter(Mandatory = $false)]
[ValidateSet(0, 'ReadOnly', 'Hidden', 'System', 'Directory', 'Archive', 'Device', 'Normal', 'Temporary', 'SparseFile', 'ReparsePoint', 'Compressed', 'Offline', 'NotContentIndexed', 'Encrypted', 'IntegrityStream', 'NoScrubData')]
[string[]]
$AttributesToSkip = @('Hidden', 'System'),
# sets the match type
[Parameter(Mandatory = $false)]
[string]
[ValidateSet('Simple', 'Win32')]
$MatchType,
# sets a value that indicates the maximum directory depth to recurse while enumerating (RecurseSubdirectories (-Recurse) must be to true)
[Parameter(Mandatory = $false)]
[int32]
$Depth,
# if given, return the special directory entries "." and ".."; otherwise, false
[Parameter(Mandatory = $false)]
[switch]
$IncludeSpecialDirectories,
# Regex Options, check https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regexoptions?view=net-7.0
[Parameter(Mandatory = $false)]
[ValidateSet('None', 'Compiled', 'CultureInvariant', 'ECMAScript', 'ExplicitCapture', 'IgnoreCase', 'IgnorePatternWhitespace', 'Multiline', 'NonBacktracking', 'RightToLeft', 'Singleline')]
[string[]]
$Options = @('None'),
# Using Microsoft.PowerShell.Commands.MatchInfo class (Select-String) to pre filtered highlight output
[Parameter(Mandatory = $false)]
[switch]
$Highlight,
# negates the matched pattern, so, show all results if pattern not matches
[Parameter(Mandatory = $false)]
[switch]
$NotMatch
)
# System.IO Enumeration Options
# Check https://docs.microsoft.com/de-de/dotnet/api/system.io.enumerationoptions?view=net-7.0 for more information and implementations
$EnumerationOptions = [System.IO.EnumerationOptions]::new()
$EnumerationOptions.IgnoreInaccessible = $IgnoreInaccessible
$EnumerationOptions.RecurseSubdirectories = $Recurse.IsPresent
$EnumerationOptions.MatchCasing = $MatchCasing
$EnumerationOptions.AttributesToSkip = $AttributesToSkip
if ($PSBoundParameters.ContainsKey('MatchType')) { $EnumerationOptions.MaxRecursionDepth = $MatchType }
if ($PSBoundParameters.ContainsKey('Depth')) { $EnumerationOptions.MaxRecursionDepth = $Depth; $EnumerationOptions.RecurseSubdirectories = $true }
$EnumerationOptions.ReturnSpecialDirectories = $IncludeSpecialDirectories.IsPresent
# Use specific method of class System.IO.Directory for files
$Method = 'EnumerateFiles'
# Regex matching RegexOptions
# check https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regexoptions?view=net-7.0 for more information and implementations
$Options = [Text.RegularExpressions.RegexOptions]$(($Options -join ', ').TrimEnd(', '))
try {
# if more than one string was given use foreach (so if input $Name is a string array)
foreach ($input in $Name) {
foreach ($item in [System.IO.Directory]::$($Method)($path, $input, $EnumerationOptions)) {
$file = [string]::new($item)
# Read each line using streamreader (ReadOnly Filestream) and use regex to find given pattern in each line.
# Output filename and matched line
$FileStream = [System.IO.FileStream]::new($file, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)
$Reader = [System.IO.StreamReader]::new($FileStream)
while ($Reader.EndOfStream -eq $false) {
$line = $Reader.ReadLine()
$match = [Regex]::Matches($line, $pattern, $Options)
if (-Not $NotMatch) {
if (-Not [string]::IsNullOrEmpty($match)) {
$Output = "$($file): $($line.Trim())"
if ($Highlight.IsPresent) {
if ($Options -match 'IgnoreCase') {
$Output = Select-String -InputObject $Output -Pattern $Pattern -AllMatches
} else {
$Output = Select-String -InputObject $Output -Pattern $Pattern -AllMatches -CaseSensitive
}
}
Write-Output $Output
}
} elseif ([string]::IsNullOrEmpty($match)) {
$Output = "$($file): $($line.Trim())"
Write-Output $Output
}
}
$Reader.Close()
$Reader.Dispose()
$FileStream.Close()
$FileStream.Dispose()
}
}
} catch {
throw $_.Exception.Message
}
}
Set-Alias -Name psgrep -Value Find-ItemContent -Force