'
' Copyright (c) 2006 Microsoft Corporation. All rights reserved.
'
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
' ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
' THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
' PARTICULAR PURPOSE.
'
Imports System
Imports System.Text.RegularExpressions
Imports System.Collections
Imports System.Collections.ObjectModel
Imports Microsoft.VisualBasic
Imports System.Management.Automation
Imports System.Management.Automation.Provider
Imports System.ComponentModel
Namespace Microsoft.Samples.PowerShell.Commands
#Region "SelectStringCommand"
'''
''' A cmdlet to search through PSObjects for particular patterns.
'''
'''
''' Can be used to search any object like a file or a variable
''' whose provider exposes methods for reading and writing
''' contents
'''
_
Public Class SelectStringCommand
Inherits PSCmdlet
#Region "Parameters"
'''
''' The Path of objects(files) to be searched
''' for the specified string/pattern.
'''
''' Path of the object(s) to search
_
Public Property Path() As String()
Get
Return paths
End Get
Set(ByVal value As String())
paths = value
End Set
End Property
Private paths() As String
'''
''' The pattern(s) used to find a match from the string
''' representation of the object. A result will be returned
''' if either of the patterns match (OR matching)
'''
'''
''' The patterns will be compiled into an array of wildcard
''' patterns if its a simple match (literal string matching)
''' else it will be converted into an array of compiled
''' regular expressions.
'''
''' Array of patterns to search.
_
Public Property Pattern() As String()
Get
Return patterns
End Get
Set(ByVal value As String())
patterns = value
End Set
End Property
Private patterns() As String
Private regexPattern() As Regex
Private wildcardPattern() As WildcardPattern
'''
''' A script block to call to perform the matching operations
''' instead of the matching performed by the Cmdlet
'''
''' Script block that will be called for matching
_
Public Property Script() As ScriptBlock
Get
Return myScript
End Get
Set(ByVal value As ScriptBlock)
myScript = value
End Set
End Property
Private myScript As ScriptBlock = Nothing
'''
''' If set, match pattern string literally.
''' If not (default), search using pattern as a Regular
''' Expression
'''
''' True if matching literally
_
Public Property SimpleMatch() As SwitchParameter
Get
Return mySimpleMatch
End Get
Set(ByVal value As SwitchParameter)
mySimpleMatch = value
End Set
End Property
Private mySimpleMatch As Boolean
'''
''' If true, then do case-sensitive searches. False by default.
'''
''' True, if case-sensitive searches are made
_
Public Property CaseSensitive() As SwitchParameter
Get
Return myCaseSensitive
End Get
Set(ByVal value As SwitchParameter)
myCaseSensitive = value
End Set
End Property
Private myCaseSensitive As Boolean
'''
''' Allows to include particular files. Files not matching
''' one of these (if specified) are excluded.
'''
_
Public Property Include() As String()
Get
Return includeStrings
End Get
Set(ByVal value As String())
includeStrings = value
ReDim myInclude(includeStrings.Length - 1)
Dim i As Integer
For i = 0 To includeStrings.Length - 1
myInclude(i) = New WildcardPattern( _
includeStrings(i), WildcardOptions.IgnoreCase)
Next i
End Set
End Property
Friend includeStrings As String() = Nothing
Friend myInclude As WildcardPattern() = Nothing
'''
''' Allows to exclude particular files. Files matching
''' one of these (if specified) are excluded.
'''
_
Public Property Exclude() As String()
Get
Return excludeStrings
End Get
Set(ByVal value As String())
excludeStrings = value
ReDim myExclude(excludeStrings.Length - 1)
Dim i As Integer
For i = 0 To excludeStrings.Length - 1
myExclude(i) = New WildcardPattern( _
excludeStrings(i), WildcardOptions.IgnoreCase)
Next i
End Set
End Property
Friend excludeStrings() As String
Friend myExclude() As WildcardPattern
#End Region
#Region "Overrides"
'''
''' If regular expressions are used for pattern matching,
''' then build an array of compiled regular expressions
''' at startup.This increases performance during scanning
''' operations when simple matching is not used.
'''
Protected Overrides Sub BeginProcessing()
WriteDebug("Validating patterns.")
If Not (patterns Is Nothing) Then
Dim pattern As String
For Each pattern In patterns
If pattern Is Nothing Then
ThrowTerminatingError(New ErrorRecord( _
New ArgumentNullException( _
"Search pattern cannot be null."), _
"NullSearchPattern", _
ErrorCategory.InvalidArgument, pattern))
End If
Next pattern
WriteVerbose("Search pattern(s) are valid.")
' If it's not a simple match, then
' compile the regular expressions once.
If Not simpleMatch.ToBool() Then
WriteDebug("Compiling search regular expressions.")
Dim regexOptions As RegexOptions = RegexOptions.Compiled
If Not caseSensitive.ToBool() Then
regexOptions = regexOptions Or RegexOptions.IgnoreCase
End If
regexPattern = New Regex(patterns.Length) {}
Dim i As Integer
For i = 0 To patterns.Length - 1
Try
regexPattern(i) = New Regex(patterns(i), regexOptions)
Catch ex As ArgumentException
ThrowTerminatingError(New ErrorRecord(ex, _
"InvalidRegularExpression", _
ErrorCategory.InvalidArgument, patterns(i)))
End Try
Next i
'loop through patterns to create RegEx objects
WriteVerbose("Pattern(s) compiled into regular expressions.")
' if not a simple match
' If it's a simple match, then compile the
' wildcard patterns once
Else
WriteDebug("Compiling search wildcards.")
Dim wildcardOptions As WildcardOptions = _
WildcardOptions.Compiled
If Not caseSensitive.ToBool() Then
wildcardOptions = wildcardOptions Or _
WildcardOptions.IgnoreCase
End If
wildcardPattern = New WildcardPattern(patterns.Length) {}
Dim i As Integer
For i = 0 To patterns.Length - 1
wildcardPattern(i) = New WildcardPattern( _
patterns(i), wildcardOptions)
Next i
WriteVerbose("Pattern(s) compiled into wildcard expressions.")
End If ' if match is a simple match
End If
' if valid patterns are available
End Sub 'BeginProcessing
' end of function BeginProcessing()
'''
''' Process the input and search for the specified patterns
'''
Protected Overrides Sub ProcessRecord()
Dim lineNumber As UInt64 = 0
Dim result As MatchInfo
Dim nonMatches As New ArrayList()
' Walk the list of paths and search the contents for
' any of the specified patterns
Dim psPath As String
For Each psPath In paths
' Once the filepaths are expanded, we may have more than one
' path, so process all referenced paths.
Dim path As PathInfo
For Each path In _
SessionState.Path.GetResolvedPSPathFromPSPath(psPath)
WriteVerbose(("Processing path " + path.Path))
' Check if the path represented is one to be excluded
' if so continue
If Not MeetsIncludeExcludeCriteria(path.ProviderPath) Then
GoTo ContinueForEach2
End If
' Get the content reader for the item(s) at the
' specified path
Dim readerCollection As Collection(Of IContentReader) = _
Nothing
Try
readerCollection = _
Me.InvokeProvider.Content.GetReader(path.Path)
Catch ex As PSNotSupportedException
WriteError(New ErrorRecord(ex, _
"ContentAccessNotSupported", _
ErrorCategory.NotImplemented, path.Path))
Return
End Try
Dim reader As IContentReader
For Each reader In readerCollection
' Reset the line number for this path.
lineNumber = 0
' Read in a single block (line in case of a file)
' from the object.
Dim items As IList = reader.Read(1)
' Read and process one block(line) at a time until
' no more blocks(lines) exist
While Not (items Is Nothing) AndAlso items.Count = 1
' Increment the line number each time a line is
' processed.
lineNumber += 1
Dim message As String = String.Format( _
"Testing line {0} : {1}", lineNumber, items(0))
WriteDebug(message)
result = SelectString(items(0))
If Not (result Is Nothing) Then
result.Path = path.Path
result.LineNumber = lineNumber
WriteObject(result)
Else
' Add the block(line) that did notmatch to the
' collection of non-matches, which will be stored
' in the SessionState variable $NonMatches
nonMatches.Add(items(0))
End If
' Get the next line from the object.
items = reader.Read(1)
End While
Next reader
ContinueForEach2: ' read and process one line at a time
Next path ' loop through the reader collection
Next psPath ' process all referenced paths
' walk the list of paths
' Store the list of non-matches in the
' session state variable $NonMatches.
Try
Me.SessionState.PSVariable.Set("NonMatches", nonMatches)
Catch ex As SessionStateUnauthorizedAccessException
WriteError(New ErrorRecord(ex, _
"CannotWriteVariableNonMatches", _
ErrorCategory.InvalidOperation, nonMatches))
End Try
End Sub 'ProcessRecord
' protected override void ProcessRecord()
#End Region
#Region "PrivateMethods"
'''
''' Check for a match using the input string and the pattern(s)
''' specified.
'''
''' The string to test.
''' MatchInfo object containing information about
''' result of a match
Private Function SelectString(ByVal input As Object) As MatchInfo
Dim line As String = Nothing
Try
' Convert the object to a string type
' safely using language support methods
line = CStr(LanguagePrimitives.ConvertTo(input, GetType(String)))
line = line.Trim(" "c, vbTab)
Catch ex As PSInvalidCastException
WriteError(New ErrorRecord(ex, _
"CannotCastObjectToString", _
ErrorCategory.InvalidOperation, input))
Return Nothing
End Try
Dim result As MatchInfo = Nothing
' If a scriptblock has been specified, call it
' with the path for processing. It will return
' one object.
If Not (script Is Nothing) Then
WriteDebug("Executing script block.")
Dim psObjects As Collection(Of PSObject) = _
script.Invoke(line, simpleMatch, caseSensitive)
Dim psObject As PSObject
For Each psObject In psObjects
If LanguagePrimitives.IsTrue(psObject) Then
result = New MatchInfo()
result.Line = line
result.IgnoreCase = Not caseSensitive.ToBool()
Exit For
End If
Next psObject
' if script block exists
' See if this line matches any of the match
' patterns.
Else
Dim patternIndex As Integer = 0
While patternIndex < patterns.Length
If simpleMatch.ToBool() AndAlso _
wildcardPattern(patternIndex).IsMatch(line) OrElse _
(Not (regexPattern Is Nothing) AndAlso _
regexPattern(patternIndex).IsMatch(line)) Then
result = New MatchInfo()
result.IgnoreCase = Not caseSensitive.ToBool()
result.Line = line
result.Pattern = patterns(patternIndex)
Exit While
End If
patternIndex += 1
End While
End If ' loop through patterns and do a match
' no script block specified
Return result
End Function 'SelectString
' end of SelectString
'''
''' Check whether the supplied name meets the include/exclude criteria.
''' That is - it's on the include list if there is one and not on
''' the exclude list if there was one of those.
'''
''' path to validate
''' True if the path is acceptable.
Private Function MeetsIncludeExcludeCriteria(ByVal path As String) _
As Boolean
Dim ok As Boolean = False
' see if the file is on the include list...
If Not (Me.include Is Nothing) Then
Dim patternItem As WildcardPattern
For Each patternItem In myInclude
If patternItem.IsMatch(path) Then
ok = True
Exit For
End If
Next patternItem
Else
ok = True
End If
If Not ok Then
Return False
End If
' now see if it's on the exclude list...
If Not (myExclude Is Nothing) Then
Dim patternItem As WildcardPattern
For Each patternItem In myExclude
If patternItem.IsMatch(path) Then
ok = False
Exit For
End If
Next patternItem
End If
Return ok
End Function 'MeetsIncludeExcludeCriteria 'MeetsIncludeExcludeCriteria
#End Region
End Class 'SelectStringCommand
#End Region
#Region "MatchInfo"
'''
''' Class representing the result of a pattern/literal match
''' that will be returned by the select-str command
'''
Public Class MatchInfo
'''
''' Indicates if the match was done ignoring case.
'''
''' True if case was ignored.
Public Property IgnoreCase() As Boolean
Get
Return myIgnoreCase
End Get
Set(ByVal value As Boolean)
myIgnoreCase = value
End Set
End Property
Private myIgnoreCase As Boolean
'''
''' Returns the number of the matching line.
'''
''' The number of the matching line.
Public Property LineNumber() As UInt64
Get
Return myLineNumber
End Get
Set(ByVal value As UInt64)
myLineNumber = value
End Set
End Property
Private myLineNumber As UInt64
'''
''' Returns the text of the matching line.
'''
''' The text of the matching line.
Public Property Line() As String
Get
Return myLine
End Get
Set(ByVal value As String)
myLine = value
End Set
End Property
Private myLine As String
'''
''' The full path of the object(file) containing the matching line.
'''
'''
''' It will be "inputStream" if the object came from the input
''' stream.
'''
''' The path name
Public Property Path() As String
Get
Return myPath
End Get
Set(ByVal value As String)
pathSet = True
myPath = value
End Set
End Property
Private myPath As String
Private pathSet As Boolean
'''
''' Returns the pattern that was used in the match.
'''
''' The pattern string
Public Property Pattern() As String
Get
Return myPattern
End Get
Set(ByVal value As String)
myPattern = value
End Set
End Property
Private myPattern As String
Private Const MatchFormat As String = "{0}:{1}:{2}"
'''
''' Returns the string representation of this object. The format
''' depends on whether a path has been set for this object or
''' not.
'''
'''
''' If the path component is set, as would be the case when
''' matching in a file, ToString() would return the path, line
''' number and line text. If path is not set, then just the
''' line text is presented.
'''
''' The string representation of the match object
Public Overrides Function ToString() As String
If pathSet Then
Return String.Format( _
System.Threading.Thread.CurrentThread.CurrentCulture, _
MatchFormat, Me.path, Me.lineNumber, Me.line)
Else
Return Me.line
End If
End Function 'ToString
End Class 'MatchInfo
#End Region
#Region "PowerShell snap-in"
'''
''' Create this sample as a PowerShell snap-in
'''
_
Public Class SelectStringPSSnapIn
Inherits PSSnapIn
'''
''' Create an instance of the SelectStrPSSnapin
'''
Public Sub New()
End Sub 'New
'''
''' Get a name for this PowerShell snap-in. This name will be used
''' in registering this PowerShell snap-in.
'''
Public Overrides ReadOnly Property Name() As String
Get
Return "SelectStrPSSnapIn"
End Get
End Property
'''
''' Vendor information for this PowerShell snap-in.
'''
Public Overrides ReadOnly Property Vendor() As String
Get
Return "Microsoft"
End Get
End Property
'''
''' Gets resource information for vendor. This is a string of format:
''' resourceBaseName,resourceName.
'''
Public Overrides ReadOnly Property VendorResource() As String
Get
Return "SelectStrSnapIn,Microsoft"
End Get
End Property
'''
''' Description of this PowerShell snap-in.
'''
Public Overrides ReadOnly Property Description() As String
Get
Return "This is a PowerShell snap-in that " & _
"includes the select-str cmdlet."
End Get
End Property
End Class 'SelectStringPSSnapIn
#End Region
End Namespace