2

When I run the following script from a newly opened PowerShell console, the loop exits so there is clearly a match, but the $matches variable (and thus $matches.PORT) is not populated the first time around. When the script is run again, it is populated.

./ssh.ps1

$BLOCK = { az webapp create-remote-connection --subscription <MY-SUBSCRIPTION> --resource-group <RESOURCE-GROUP> -n <NAME> }
$global:CONNECTION = Start-ThreadJob -ScriptBlock $BLOCK

$done = 0
$match_string = ".*Opening tunnel on port: (?<PORT>\d{1,5})\b.*"

while ($done -lt 1) {
    if ($CONNECTION.HasMoreData)
    {
        $DATA = Receive-Job $CONNECTION 2>&1
        
        if ($DATA -match $match_string)
        {
            $port = $matches.PORT
            Write-Output "Connection open on port $port."
            $done = 1
        }
    }
}
Write-Output "Loop ended."
exit

Output in the PowerShell console is:

PS <LOCAL-DIR>> ./ssh
Connection open on port .
Loop ended.
PS <LOCAL-DIR>> ./ssh
Connection open on port 63182.
Loop ended.

By contrast, when I try running the following script, $matches is populated the first time it is run.

./match.ps1

$string1 = "hello, hello, you big beautiful world of wonder!"
$match_str = ".*\b(?<gotcha>world)\b.*"

$done = 0

while ($done -lt 1)
{
    if ($string1 -match $match_str)
    {
        write-output "Matches:"
        write-output $matches
        
        $done = 1
    }
}

Output:

PS <LOCAL-DIR>> ./match
Matches:

Name                           Value
----                           -----
gotcha                         world
0                              hello, hello, you big beautiful world of wonder!

If anyone can fathom why the text is matched in the first script without $matches being populated I would be incredibly grateful.

P.S. The script existing after the loop is just for investigative purposes and not what my code will actually do.

P.P.S. For reference, the output from az webapp create-remote-connection, after a delay whilst connecting, is:

Verifying if app is running....
App is running. Trying to establish tunnel connection...
Opening tunnel on port: 63341
SSH is available { username: root, password: Docker! }
Ctrl + C to close

(The port varies each time.)

R160K
  • 283
  • 2
  • 10

1 Answers1

3

If the automatic $Matches variable isn't populated after a -match operation, the implication is that the LHS operand was a collection rather than a single string.

Therefore, loop over the value of $DATA and match each line individually:

foreach ($line in $DATA) {
  if ($line -match $match_string)
  {
    $port = $matches.PORT
    "Connection open on port $port."
    $done = 1
    break
  }
}

By design:

  • $Matches is only populated if the LHS is a string (scalar).
  • With a collection (array) as the LHS, -match - as many comparison operators do - acts as a filter and returns the (potentially empty) sub-array of matching elements.
  • Any prior $Matches value is preserved if either a given string-scalar -match operation happens not to find a match or its LHS is a collection.
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Thanks - had just been experimenting just now and discovered that the first time the script is run, $DATA is multiline, whereas for some reason all subsequent runs seem to be a single-line string. Have now added a foreach loop to my logic and all is working now. – R160K Nov 12 '21 at 23:36