none
Understand piece of code with -match operator

    Question

  • Could you please help me to understand piece of code from the example from  Bruce Payette's book "Powershell in Action".

    It is The web server script (Listing 11.9) Invoke-WebServer.ps1, page 365, the place where it parses an HTTP respong from a client, but it's kind of working with strings. 

    . . . . 

    $received contans request from a client in form like:

    GET /2+3 HTTP/1.1
    Accept: text/html, application/xhtml+xml, */*
    Accept-Language: en-US,en;q=0.7,ru;q=0.3
    User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
    Accept-Encoding: gzip, deflate
    Host: localhost:8080
    DNT: 1
    Connection: Keep-Alive

    $received = [regex]::split($received, "`r`n") # split strings I understood
    $received = @($received -match "GET")[0] # here is the problem for me 

    As far as I understand ($received -match "GET") returns $true or $false, how we can get an array from it, and take the first element like this: $received = @($received -match "GET")[0] 

    furher goes even more confusing part ...

    if ($received) #we use it as bool, I undertand 
    {
    $expression = $received -replace "GET */" –replace 'HTTP.*$' -replace '%20',' ' # now we treat $received as a string ??? it was a bool? which -replace gets executed first? first, second or third? 

    . . . . . 

    Thanks in advance!

    Friday, November 01, 2013 1:58 PM

Answers

  • Comparison operators (including -match) behave differently if the left operand is a collection.  In this case, that is guaranteed, because the output of [regex]::split() is always an array.

    When the left operand of -match is a collection, it outputs any elements from the collection that match the pattern.  For example:

    $strings = "Dog","Cat","Doghouse","Cat Burglar"
    
    $test = $strings -match "Dog"
    $test
    
    # $test is currently an array containing "Dog" and "Doghouse"
    
    $test = $strings -match "Cat"
    $test
    
    # $test is now an array containing "Cat" and "Cat Burglar"

    There's more information on this topic in the about_Comparison_Operators help file (and very likely elsewhere in the book as well.)
    Friday, November 01, 2013 2:09 PM
  • Yep, left to right:

    $string = 'testing'
    $string -replace 'testing','dog' -replace 'dog','cat' -replace 'cat','whatever'
    
    # whatever

    Saturday, November 02, 2013 5:12 AM

All replies

  • Comparison operators (including -match) behave differently if the left operand is a collection.  In this case, that is guaranteed, because the output of [regex]::split() is always an array.

    When the left operand of -match is a collection, it outputs any elements from the collection that match the pattern.  For example:

    $strings = "Dog","Cat","Doghouse","Cat Burglar"
    
    $test = $strings -match "Dog"
    $test
    
    # $test is currently an array containing "Dog" and "Doghouse"
    
    $test = $strings -match "Cat"
    $test
    
    # $test is now an array containing "Cat" and "Cat Burglar"

    There's more information on this topic in the about_Comparison_Operators help file (and very likely elsewhere in the book as well.)
    Friday, November 01, 2013 2:09 PM
  • From my understanding of looking at this, $received is a string.

    $received = @($received -match "GET")[0]

    First $received -match "GET" goes through the original received string which is a http response, and looks for GET, this creates an array object, so we want to retrive the first item $($received -match "GET")[0]. So now $received is a string containing "GET /2+3 HTTP/1.1"

    The if($received) still works because the string is not empty. You can do a quick test with the following

    $received = "Some Text"
    If ($received)
    {
      Write-Host $true
    }
    else
    {
      Write-Host $false
    }
    You initially run it and it will be true, take Some Text out, re-run it and it is false

    If you find that my post has answered your question, please mark it as the answer. If you find my post to be helpful in anyway, please click vote as helpful.

    Don't Retire Technet

    Friday, November 01, 2013 2:14 PM
  • This gets a little confusing to talk about because he keeps assigning results back to the $received variable.  Here's the code rewritten to use multiple variable names, making it easier to discuss:

    $received = @'
    GET /2+3 HTTP/1.1
    Accept: text/html, application/xhtml+xml, */*
    Accept-Language: en-US,en;q=0.7,ru;q=0.3
    User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
    Accept-Encoding: gzip, deflate
    Host: localhost:8080
    DNT: 1
    Connection: Keep-Alive 
    '@
    
    $lines = [regex]::split($received, "`r`n")
    $matchingLine = @($lines -match "GET")[0]
     
    if ($matchingLine)
    {
        $expression = $matchingLine -replace "GET */" –replace 'HTTP.*$' -replace '%20',' '
    } 

    Now, $received is a string which may contain multiple lines. $lines is an array containing at least one element, one for each line of $received. $matchingLine is either a string (if a line containing "GET" was found), or $null (if not).  $matchingLine will evaluate to True in a conditional if it is a non-empty string, and to False if it's null.

    Inside the If block, $matchingLine is guaranteed to be a non-empty string (containing "GET"), so it can be passed to the -replace operator again.

    Hope that helps.  :)


    Friday, November 01, 2013 2:22 PM
  • Thanks a lot guys, for your answers, now I understand, but what about multiple "-replace"? How these will get executed? From left to right or vise versa?
    Friday, November 01, 2013 8:17 PM
  • Yep, left to right:

    $string = 'testing'
    $string -replace 'testing','dog' -replace 'dog','cat' -replace 'cat','whatever'
    
    # whatever

    Saturday, November 02, 2013 5:12 AM
  • Thanks again David!
    Saturday, November 02, 2013 7:22 AM