0

I would like to capture the value from different regexs in order to build a file list of my endpoint health checks.

I have some PS Script that parses and pull information from some txt file correctly, but I want to use the values captured in the different regexs within my script to compose new strings.

My script below read from txt file #1 and output txt file #2, all correctly.

PS Script

(Get-Content 'c:\fake_path\test_stack.txt') `
      | Select-String '(^\S{5}SERVER:)(\S*)(\S{5})','(^\S*n:\"\*:)(\d*)(:"\s$)','(^\"\/)(\S*)("\s$)' -AllMatches | Foreach-Object {$_.Matches} | 
       Foreach-Object {$_.Groups[2].Value} |
       Out-File c:\fake_path\demo_stack.txt

1 InFile

*****SERVER:WIN001*****
SITE.NAME:"PRJ_ABC"
bindingInformation:"*:80:" 
"/App001" 
*****SERVER:WIN002*****
SITE.NAME:"PRJ_DEF"
bindingInformation:"*:81:" 
"/App001" 
SITE.NAME:"PRJ_HIJ"
bindingInformation:"*:82:" 
"/App001" `

2 OutFile

WIN001
80
App001
WIN002
81
App001
82
App001`

However I'm hoping to use the values that are captured in my 3 different regexs below into 3 different variables, let's say $a, $b and $c.

(Select-String '(^\S{5}SERVER:)(\S*)(\S{5})','(^\S*n:\"\*:)(\d*)(:"\s$)','(^\"\/)(\S*)("\s$)')

My try:

Foreach-Object 'http://' + 'a.{$_.Groups[2].Value}' + ':' + '$b.{$_.Groups[2].Value}' + '/' + '$c.{$_.Groups[2].Value}' + '/api/status'

Desired output of my try (written above)

A new file composed like this: http://WIN001:80/App001/api/status http://WIN002:81/App001/api/status http://WIN002:81/App001/api/status So as you can see I wanted to capture the regex in variables $a $b and $c, then concatenate strings like http:// + $a + ':' + $b + ..... and so for, in order to format it and produce a url.

I believe this is feasible, just lacking the background.

Welcome all the advices, cheers ;-)

2 Answers 2

2

I'm following a more simple approach,

  • each input line is checked against 3 quite simple Regular Expressions with named capture groups,
  • if the 1st two match, a variable is set,
  • if the last matches the url with the gathered data is output:

## Q:\Test\2019\04\12\SO_55655078.ps1

(Get-Content '.\test_stack.txt')| ForEach-Object {
    if($_ -match 'SERVER:(?<server>[^*]+)'){$Server = $Matches.Server}
    if($_ -match ':(?<port>\d{2,5}):'     ){$Port   = $Matches.Port}
    if($_ -match '^\"(?<App>[^"]+)\"'     ){
       "http://{0}:{1}{2}/api/status" -f $Server,$Port,$Matches.App
    }
} # | Set-Content '.\demo_stack.txt'

Sample output:

> Q:\Test\2019\04\12\SO_55655078.ps1
http://WIN001:80/App001/api/status
http://WIN002:81/App001/api/status
http://WIN002:82/App001/api/status

BTW your 3rd example line is wrong, it should have port 82.

Sign up to request clarification or add additional context in comments.

2 Comments

I ran your script and This definetely solved my issue, Big Thanks :-), I had the chance to learn about capturing text match into group name (the same with @AdminOfThings). Allow me to ask you few questions: The IF statements within de ForEach-Object becomes and array?, Because I can see this {0}:{1}{2}. Then I see right after an option or switch "-f", What does it mean?. PS: Yes, my 3rd example line was wrong, Very good Catch!!! :-D
@farp332 No the if commands are processed one after the other for each line of the file, as described the 1st two ensure that $Server,$Port have the actual value when the 3rd triggers the output with the -f Format operator The {x} in the format string denote the positions where the following expressions/variables are inserted.
2

This is a bit ugly, but it works:

$file = Get-Content "c:\somefile.txt" -Raw
ForEach ($entry in ($file -split "(?m)^\*{5}").trim()) {
    $server = (Select-String -InputObject $entry -Pattern "(?m)(?<=SERVER:)(?<Server>.*?)(?=\*+.*$)" -AllMatches).matches.value -as [array]
    $ports = (Select-String -InputObject $entry -Pattern "(?m)(?<=:`".*?:)(?<port>\d+)(?=:`".*$)" -AllMatches).matches.value -as [array]
    $apps = (Select-String -InputObject $entry -Pattern "(?m)(?<=^`")(?<app>/.*?)(?=`".*$)" -AllMatches).matches.value -as [array]
    for ($i = 0; $i -lt $ports.count; $i++) {
        "http://" + $server + ":" + $ports[$i] + $apps[$i] + "/api/status" | Out-File -FilePath "NewFile.txt" -Append
    }
}

I used the -Split method to divide the file by the server entries. Each of those entries is an array element that is stored in $entry as the loop iterates. I am splitting by the matching any line that begins with 5 * characters (^\*{5}). The .trim() method just removes blank lines in the middle of the split output.

$server is an array assigned the matched text from entry that represents the server name.

  • (?m) is the multiline modifier. This is so that ^ and $ will match the start and end of each line.
  • (?<=SERVER:) is a positive lookbehind to start matching at the position that proceeds the string SERVER:.
  • (?<Server>.*?) is the capture group named Server that holds the server name string.
  • (?=\*+.*$) is a positive lookahead for one or more * characters followed by any characters and then the end of the line.

$ports is an array assigned all of the port numbers in your bindings attribute.

  • (?<=:`".*?:) is the positive lookbehind for :", followed by a few characters as possible, and followed by :
  • (?<port>\d+) is the named capture group port that represents your port number
  • (?=:`".*$) is the positive lookahead for :" and then any characters until the end of the line

$apps is the array assigned all of the application directories that exist alone on a line

  • (?<=^`") is a positive lookbehind for a line beginning with "
  • (?<app>/.*?) is the capture group app that represents the matched application paths without quotes. It captures the / and any characters until it reaches a "
  • (?=`".*$) is a positive lookahead checking for a " followed by any characters until the end of the line

The for loop assumes that there are as many port numbers as there are application paths per server. The reason each variable above is an array is so that the first element [0] can be accessed with the same syntax as any other element when there may only be one element present. The loop outputs a string concatenation with each iteration potentially outputting a different port and application path combination. Each string is output to NewFile.txt.

3 Comments

I first followed a similar complex approach splitting the input into server sections, until I realized that maintaining variables of current matches and outputting with Apps is much more simple.
@LotPings, your approach does seem better. I had started with a bunch of splits and then reverted back to my usual regex complexity. +1 for your approach.
@AdminOfThings Thanks a lot for the help and the master explanation of what you did. I ran you script and it seems that it is not iterating with the whole servers list. It takes just 2 servers, I couldn't spot the issue. I'm very curious of What could have happened?, cheers :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.