Home Dashboard Directory Help
Search

Select-String's doesn't work with multiple pattern matches on a single line by Graimer0


Status: 

Active


6
0
Sign in
to vote
Type: Bug
ID: 808786
Opened: 11/17/2013 7:54:40 AM
Access Restriction: Public
3
Workaround(s)
view
3
User(s) can reproduce this bug

Description

When you use an array of patterns with Select-String, the cmdlet doesn't behave as expected. If you have a line that matches multiple patterns, it only displays the first match. I then though -AllMatches might help, but that didn't work either.

Get-Help Select-String -Parameter AllMatches

-AllMatches [<SwitchParameter>]
    Searches for more than one match in each line of text.
    ....
    When Select-String finds more than one match in a line of text, it still emits only one MatchInfo object for the line, but the Matches proper
    ty of the object contains all of the matches.

When you read this definition, you get the idea that this switch will make it match all patterns in a single line, but that's not the case. It only finds all matches of the first matched pattern.
Details
Sign in to post a comment.
Posted by Joel 'Jaykul' Bennett on 5/19/2014 at 9:36 AM
Passing an array of patterns is completely broken, because you'll only ever get the first match on any given line. The workarounds mentioned involve writing regular expressions, so they cannot help if you're already using complex expressions or if you wanted to use -SimpleMatch ... and in any case they corrupt the "Pattern" property of the MatchInfo output objects:

For example: You can see that "four" never shows up in the matches, and "way" only shows up once, instead of twice. Changing this to a compound regex pattern like "one\S*|way|four" would find all the matches, but would yield a useless pattern property on the output:

> "There is only one way","No way, there are four!" | Select-String -Pattern "on\S*","way","four" -AllMatches | Select Pattern -expand Matches
Sign in to post a workaround.
Posted by David Wyatt on 5/19/2014 at 10:08 AM
Possible proxy function workaround which keeps the Pattern properties of the output objects intact. Generated and tested on PowerShell 4.0:

function Select-String
{
    [CmdletBinding(DefaultParameterSetName='File', HelpUri='http://go.microsoft.com/fwlink/?LinkID=113388')]
     param(
        [Parameter(ParameterSetName='Object', Mandatory=$true, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [AllowNull()]
        [psobject]
        ${InputObject},
    
         [Parameter(Mandatory=$true, Position=0)]
        [string[]]
        ${Pattern},
    
        [Parameter(ParameterSetName='File', Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)]
         [string[]]
        ${Path},
    
        [Parameter(ParameterSetName='LiteralFile', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [Alias('PSPath')]
        [string[]]
         ${LiteralPath},
    
        [switch]
        ${SimpleMatch},
    
        [switch]
        ${CaseSensitive},
    
        [switch]
        ${Quiet},
    
        [switch]
        ${List},
    
        [ValidateNotNullOrEmpty()]
        [string[]]
        ${Include},
    
        [ValidateNotNullOrEmpty()]
        [string[]]
        ${Exclude},
    
        [switch]
        ${NotMatch},
    
        [switch]
        ${AllMatches},
    
        [ValidateNotNullOrEmpty()]
        [ValidateSet('unicode','utf7','utf8','utf32','ascii','bigendianunicode','default','oem')]
         [string]
        ${Encoding},
    
        [ValidateNotNullOrEmpty()]
        [ValidateRange(0, 2147483647)]
        [ValidateCount(1, 2)]
        [int[]]
        ${Context})
    
    begin
     {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
            {
                $PSBoundParameters['OutBuffer'] = 1
             }

            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Select-String', [System.Management.Automation.CommandTypes]::Cmdlet)
            
            $null = $PSBoundParameters.Remove('Pattern')
            
            $steppablePipelines =
            foreach ($string in $Pattern)
            {
                $scriptCmd = { & $wrappedCmd @PSBoundParameters -Pattern $string }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                 $steppablePipeline.Begin($PSCmdlet)

                $steppablePipeline
            }
        } catch {
            throw
        }
    }
    
    process
    {
        try {
             foreach ($steppablePipeline in $steppablePipelines)
            {
                $steppablePipeline.Process($_)
            }
        } catch {
            throw
        }
    }
    
     end
    {
        try {
            foreach ($steppablePipeline in $steppablePipelines)
            {
                $steppablePipeline.End()
            }
        } catch {
            throw
         }
    }
    <#
    
    .ForwardHelpTargetName Select-String
    .ForwardHelpCategory Cmdlet
    
    #>
    
}
Posted by Dardenne Laurent on 11/25/2013 at 9:35 AM
#Or
$Patterns=@("test",'o2$')
$ofs='|'
Select-String -Path C:\Temp\TestData.txt -Pattern "$Patterns" -AllMatches|fl *
Posted by Dardenne Laurent on 11/25/2013 at 9:30 AM
@"
test2 test
test2 hello2
"@ > C:\Temp\TestData.txt

Select-String -Path C:\Temp\TestData.txt -Pattern "test|o2$" -AllMatches|fl *