Converting $matches to PSCustomObject - regex

I had created this function to attempt to break strings into groups of tokens that can be made into PowerShell object. This is what I have so far. If I use the code outside of the function I get the desired result but if I attempt to use it within the function I get nothing. I believe the output is disappearing. I know I am not using the right terminology to describe what is happening. Any assistance would be appreciated.
Function Convert-MatchToPSCustomObject
{
<#
.SYNOPSIS
Converts $matches to PSCustomObject
.DESCRIPTION
Takes an input string and converts it to PSCustomObject
.PARAMETER Pattern
Mandatory. Pattern which to match within the string
.PARAMETER String
Mandatory. Input String
.EXAMPLE
Convert-MatchToPSCustomObject -Pattern '(?<descriptor>^\b[A-Z]{2})(?>[=])(?<value>[\w \.\-]+\b$)' -String 'CN=Only Da Best'
.LINK
https://regex101.com/r/1hYb2J/1/
.LINK
https://vexx32.github.io/2018/11/08/Named-Regex-Matches-PSCustomObject/
.NOTES
Version: 1.0
Author: Kino Mondesir
#>
[cmdletbinding()]
param
(
[Parameter(HelpMessage="Pattern to match", Position=0, Mandatory=$false, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullorEmpty()]
[string]$pattern = '(?<descriptor>^\b[A-Z]{2})(?>[=])(?<value>[\w \.\-]+\b$)',
[Parameter(HelpMessage="Input string", Position=1, Mandatory=$true, ValueFromPipeline=$true)]
[ValidateNotNullorEmpty()]
[string]$string
)
Try
{
return $string -split ',' | ForEach-Object {
if ($PSItem -match $pattern)
{
$Matches.Remove(0)
[PSCustomObject]$Matches
}
else
{
Throw "No matching items found!"
}
}
}
Catch
{
$exception = $_.Exception
Write-Error $exception.Message
return -1
}
}

Related

Why is this switch deleting an extra line (PowerShell)

This code is supposed to find a line with a regular expression and replace the line with "test". It is finding that line and replace it with "test" but also deleting the line under it, no matter what is in the next line down. I feel like I am just missing something about how a switch works in PowerShell.
Note: This is super boiled down code. There is a larger program this is part of.
$reg = '^HI\*BH'
$appendText = ''
$file = Get-ChildItem (join-path $PSScriptRoot "a.txt.BAK")
foreach ($f in $file){
switch -regex -file $f {
$reg
{
$appendText = "test"
}
default {
If ($appendText -eq '') {$appendText = $_}
$appendText
$appendText = ''
}
}
}
a.txt.BAK
HI*BH>00>D8>0*BH>00>D8>0*BH>A1>D8>0*BH>B1>D8>0000000~
HI*BE>02>>>0.00*BE>00>>>0.00~
NM1*71*1*TTT*NAME****XX*0000000~
PRV*AT*PXC*000V00000X~
Output:
test
NM1*71*1*TTT*NAME****XX*0000000~
PRV*AT*PXC*000V00000X~
The switch is not "deleting" anything - but you explicit ask it to overwrite $appendText on match, and you only ever output (and reset the value of) $appendText when it doesn't.
This code is supposed to find a line with a regular expression and replace the line with "test".
In that case I suggest you simplify your switch:
switch -regex -file $f {
$reg {
"test"
}
default {
$_
}
}
That's it - no fiddling around with variables - just output "test" on match, otherwise output the line as-is.
If you insist on using the intermediate variable, you'll need to output + reset the value in both cases:
switch -regex -file $f {
$reg {
$appendText = "test"
$appendText
$appendText = ''
}
default {
$appendText = $_
$appendText
$appendText = ''
}
}

Powershell function returns intermediate values

I'm probably misunderstanding how PowerShell functions work, but I have the following to extract the year from a string such as "abcdef20201123xyz":
function getYear($str) {
$str -match ".*(?<year>[\d]{4})[\d]{4}.*"
return $matches['year']
}
I expected getYear("abcdef20201123xyz") just to return "2020", but I get
True
2020
Why is this, and how do I just get what I put in the "return"?
In PowerShell, every not handled function call is sent to the output. So you either assign the return value to a variable, or you pipe it to Out-Null:
function getYear($str) {
$str -match ".*(?<year>[\d]{4})[\d]{4}.*" | out-null
return $matches['year']
}

How do get multiple value from text file using (Regex) in Powershell

I'm new to Regex. I'm trying to scan few text file and get some field name based on Tag Field and Action. I'm using Powershell and regex = ?smi)\b(field|Action)[^)]+ to get the full Field and action String. Once i get the string now to get the value from field and action string i call (?<=;).* However it fails (wrong value) on line Field("Waiting Period (H)"; "Waiting Period (Hrs.)") { } . Please note the Value can be in Quote or no Quote however it will always end with this ) the problem is some field name can have e.g (Hrs.)")
{
group("Default")
{
Field("Postal Code"; **Postal**)
{
ToolTip = 'Enter Postal Code ';
trigger OnValidate()
begin
If ("Postal Code" <> xRec."Postal Code.") then
function()
END;
}
field ("Cels or Fahr"; **"Celsius or Fahrenheit"**) {
ToolTip = 'Enter temperature ';
}
Field("Waiting Period (H)"; **"Waiting Period (Hrs.)"**) { }
Field ( Latitude; **Latitude**)
{
ToolTip = 'Enter Latitude';
trigger OnValidate()
}
}
}
action(**"Restore to Default"**)
{
ToolTip = 'Enter Latitude';
}
I'm looking for output for tag "Field Or Action" to return these values below
Postal
"Celsius or Fahrenheit"
"Waiting Period (Hrs.)"
Latitude
"Restore to Default"
So, If i use https://regex101.com/ on the same text (?<=;).* the text is "Waiting Period (Hrs.)") { } is wrong "Waiting Period (Hrs.)") { } . Any help or advice will be helpful.
First get the group
group("") {
field(...) {}
}
by
$mainText -match '(?smi)\{(?<groupText>\s*?group\(".*"\)\s*?\{\s*?(?:\s*?Field\s*?\((?:\s*?"*?.*?(?:\(.*?\))*?.*?"*?)?;\s*?(?:\*\*"?.*?(?:\(.*?\))*?.*?"?\*\*)?\)\s*?\{.*?\}\s*?)*?\s*?\}\s*?)\}\s*?'
and get the groupText capture $groupText = $Matches["groupText"].
$fieldMatches = $groupText | select-string -Pattern '(?smi)(?:\s*?Field\s*?\((?<fieldName1>\s*?"*?.*?(?:\(.*?\))*?.*?"*?)?;\s*?(?<fieldName2>\*\*"?.*?(?:\(.*?\))*?.*?"?\*\*)?\)\s*?\{.*?\}\s*?)+?' -AllMatches
$fieldMatches.Matches | %{ $_.Groups["fieldName1"].Value + ' <===> '+ $_.Groups["fieldName2"].Value }
The point here is to use lazy matching with *?. And we use a separate match because when there is a repeated capturing groups in a pattern only the last capture matched.
You could similarly match the action group separately.

Conditional If with REGEX

I'm working on a function to try some regex. Let me explain.
function traitement
{
if ($Matches.NAME -match "^A_(?<test1>[\w{1,6}]{1,7})")
{
[void]($memberOfCollection.add($Matches.test1))
}
elseif ($Matches.NAME -match "^A_(?<test2>[]*)")
{
[void]($memberOfCollection.add($Matches.test2))
}
else
{
[void]($memberOfCollection.add($Matches.NAME))
}
}
I have $Matches.NAME return string like "A_UserINTEL", "A_UserINTELASUS" or "A_UserINTEL_Adobe"
I need to differentiate 2 strings coming from $Matches.NAME and therefore write several tests.
"A_UserINTEL" and "A_UserINTELASUS" must return "UserINTEL".
"A_UserINTEL_Adobe" must return "UserINTEL_Adobe"
Test1 allows me to retrieve "UserINTEL" but I didn't succeed test2 to bring me "UserINTEL_Adobe".
Any idea? Thank you.
There's a;ways more ways then just one, especially when it comes to regular expressions, but here's one way:
function traitement {
# just for more clarity in the rest of the code
$name = $Matches.NAME
if ($name -match '^A_UserIntel(?:ASUS)?$') {
# the regex tests for "A_UserINTEL" or "A_UserINTELASUS"
[void]($memberOfCollection.add("UserINTEL"))
}
elseif ($name -match '^A_UserIntel_Adobe$') {
# this elseif is basically the same as
# elseif ($name -eq 'A_UserIntel_Adobe') {
# no real need for regex there..
[void]($memberOfCollection.add("UserINTEL_Adobe"))
}
else {
[void]($memberOfCollection.add($name))
}
}

Perl Regex not matching

I'm trying to match lines from a file and extract a certain part.
My Regex works with all online testers I could find but not with my perl.
I'm on version v5.10.0 and cannot update.
The regex looks like this:
sub parse_bl_line {
if ($_[0] =~ m/^copy\s+.*?\s+(.*?\_.*)/) {
return $1;
} else {
log_msg("Line discarded: $_[0]", 4);
return "0";
}
}
A couple lines of test data which should match (only the last matches):
#bl_lines = (
"copy xxxxxx_/cpu b_relCAP_R3.0-1_INT5_xxxxx_cpu_p1",
"copy xxxxxxxx_/va_xxx_parameters b_relCAP_R3.0-1_INT5_xxxxx_va_xxx_parameters_p1",
"copy xxxxxxxx_/xxxxxxx_view.tcl b_relCAP_R3.0-1_INT5_xxxxxx_view.tcl_p0",
"copy xxxxx_/xxxxxarchivetool.jar b_relEARLY_DROP1_xxxxxarchivetool.jar_xx");
And calling the function:
foreach(#bl_lines) {
$file=parse_bl_line($_);
if ($file !~ "0") {
log_msg("Line accepted: $_", 4);
log_msg("File extracted: $file", 4);
}else {
log_msg("Line rejected: $_", 2);
}
}
I'm trying to match the last part e.g.
b_relEARLY_DROP1_xxxxxarchivetool.jar_xx
Output looks the following:
20120726 13:15:34 - [XXX] ERROR: Line rejected: copy xxxxxx_/cpu b_relCAP_R3.0-1_INT5_xxxxx_cpu_p1
20120726 13:15:34 - [XXX] ERROR: Line rejected: copy xxxxxxxx_/va_xxx_parameters b_relCAP_R3.0-1_INT5_xxxxx_va_xxx_parameters_p1
20120726 13:15:34 - [XXX] ERROR: Line rejected: copy xxxxxxxx_/xxxxxxx_view.tcl b_relCAP_R3.0-1_INT5_xxxxxx_view.tcl_p0
20120726 13:15:35 - [XXX] INFO: Line accepted: copy xxxxx_/xxxxxarchivetool.jar b_relEARLY_DROP1_xxxxxarchivetool.jar_xx
20120726 13:15:35 - [XXX] INFO: File extracted: b_relEARLY_DROP1_xxxxxarchivetool.jar_xx
Hint
I did some of the testing that #BaL proposed and found out that the pattern matching works without the selection parenthesis.
if ($_[0] =~ m/^copy\s+.+?\s+.+?\_.+$/) {
The test : if ($file !~ "0") { is true when $file doesn't contain a 0 at any position which is the case of the last string only.
I guess you want to use : if ($file ne '0') { or even shorter : if ($file) {
Apart of this you should really use strict; and use warnings always.
What are you trying to match ? The last part ?
Don't use * if you know that you have something to match, use + instead :
if ($_[0] =~ m/^copy\s+.+?\s+(.\+?)$/) {
return $1;
}
I'm guessing that the last line of your test file is the only one that doesn't end with a "\n". Funny little buggers are always getting in the way.....
Change the comparison operator in your if statement from !~ to ne as you are making a string comparison. When I make this change, all log lines were accepted.
I tested this on perl 5.14.2, not 5.10, but I didn't use any special features. Give it a go! code is below:
use 5.14.2;
sub log_msg{
say shift;
}
sub parse_bl_line {
if ($_[0] =~ m/^copy\s+.*?\s+(.*?\_.*)/) {
return $1;
}
else {
log_msg("Line discarded: $_[0]", 4);
return "0";
}
}
my #bl_lines = (
"copy xxxxxx_/cpu b_relCAP_R3.0-1_INT5_xxxxx_cpu_p1",
"copy xxxxxxxx_/va_xxx_parameters b_relCAP_R3.0-1_INT5_xxxxx_va_xxx_parameters_p1",
"copy xxxxxxxx_/xxxxxxx_view.tcl b_relCAP_R3.0-1_INT5_xxxxxx_view.tcl_p0",
"copy xxxxx_/xxxxxarchivetool.jar b_relEARLY_DROP1_xxxxxarchivetool.jar_xx"
);
foreach(#bl_lines) {
my $file = parse_bl_line($_);
if ($file ne "0") { # Changed the comparison operator here
log_msg("Line accepted: $_", 4);
log_msg("File extracted: $file", 4);
}
else {
log_msg("Line rejected: $_", 2);
}
}