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 = ''
}
}
Related
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
}
}
Hi can anybody help me I am stuck and can't get regex to work with powershell and a switch statement.
Could not find anything on the web that was helpful either.
How can I filter an IP for example or a string of 7 to 8 numbers?
switch -regex ($buffer)
{
($buffer -match '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
{}
($buffer -match {'\d{7,8}'})
{}
}
When used in -regex mode, PowerShell expects the case condition to be a regex pattern, nothing else:
switch -regex ($buffer)
{
'[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
{
# looks kinda like an IP
}
'\d{7,8}'
{
# just numbers
}
}
Use braces instead of parenthesis, and omit the variable for switch altogether:
switch (1)
{
{ $buffer -match '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' }
{ Write-Output "IP Address" }
{ $buffer -match '\d{7,8}' }
{ Write-Output "7-8 digits" }
}
I have a Perl script that takes in arguments. When I had single value arguments, the following code sufficed:
switch ($ARGV[0]) {
case "--cmd1" {
$action = "cmd1";
}
case "--cmd2" {
$action = "cmd2";
}
Now, I have a case where the command, cmd3 has a parameter, as in --cmd3=SOMETHING. Since SOMETHING can vary, the simple switch/case does not work anymore. Basically, I need to do a switch/case on the command itself. I thought I could use a regex with the first matching group being the command and the second being the optional equals. The following does not work, but it illustrates what I'm trying to do.
$ARGV[0] =~ m/(.*?)(=.*){0,1}/;
my $cmd = $1;
my $equals = $2;
switch ($cmd) {
case "--cmd1" {
$action = "cmd1";
}
case "--cmd2" {
$action = "cmd2";
}
case "--cmd3" {
$action = "cmd3";
print $equals;
}
:::::::::::::::::::::::::::::::::::EDIT:::::::::::::::::::::::::::::::::::::::::::::::::::::
I figured it out, but I'll give the guy who answered upvotes and accept anyway. I could not use if because that means restructuring everything. Here's the solution.
switch ($ARGV[0]) {
case "--cmd1" {
$action = "cmd1";
}
case "--cmd2" {
$action = "cmd2";
}
case m/--cmd3(=.*)?/ {
$ARGV[0] =~ m/--cmd3(=.*)?/;
$action = "cmd3";
print $1;
}
Why aren't you using Getopt::Long?
How about something like this?
my $cmd = "";
my $equals = "";
if($ARGV[0] =~ m/^\-\-cmd1$/){$cmd="cmd1"}
if($ARGV[0] =~ m/^\-\-cmd2$/){$cmd="cmd2"}
if($ARGV[0] =~ m/^\-\-cmd3=(.*)$/){$cmd="cmd3";$equals=$1}
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);
}
}
DON'T ASK WHY but...
I have a regex that needs to be case insensitive if run on windows BUT case sensitive when run on *nix.
Here is an example snippet of what I am kind-of doing at the moment.
sub relative_path
{
my ($root, $path) = #_;
if ($os eq "windows")
{
# case insensitive with regex option 'i'
if ($path !~ /^\Q$root\E[\\\/](.*)$/i)
{
print "\tFAIL:$root not in $path\n";
}
else
{
return $1;
}
}
else
{
# case sensitive
if ($path !~ /^\Q$root\E[\\\/](.*)$/)
{
print "\tFAIL:$root not in $path\n";
}
else
{
return $1;
}
}
return "";
}
Argh! The repetition hurts my OCD but my perl-fu is weak. Somehow I want to make the regex option 'i' for case-insensitive conditional but I don't now how?
You can use an extended construct to specify the option. For example:
#!/usr/bin/env perl
use warnings; use strict;
my $s = 'S';
print check($s, 'i'), "\n";
print check($s, '-i'), "\n";
sub check {
my ($s, $opt) = #_;
return "Matched" if $s =~ /(?$opt)^s\z/;
return "Did not match";
}
See perldoc perlre.
You can create patterns and store them in scalars using the qr operator:
sub relative_path
{
my ($root, $path) = #_;
my $pattern = ($os eq "windows") ? qr/^\Q$root\E[\\\/](.*)$/i : qr/^\Q$root\E[\\\/](.*)$/;
if ($path !~ $pattern)
{
print "\tFAIL:$root not in $path\n";
}
else
{
return $1;
}
}
This might not be 100% perfect, but hopefully you should get the idea.
Make sure to check out the section "Quote and Quote-Like Operators" in perlop.
EDIT: Okay, here's a DRY solution since people are complaining about it.
sub relative_path
{
my ($root, $path) = #_;
my $base_pattern = qr/^\Q$root\E[\\\/](.*)$/;
my $pattern = ($os eq "windows") ? qr/$base_pattern/i : $base_pattern;
if ($path !~ $pattern)
{
print "\tFAIL:$root not in $path\n";
}
else
{
return $1;
}
}
In addition to achieving the stated objective, this properly handles volumes unlike the regex patterns previously posted.
use Path::Class qw( dir );
sub relative_path {
my ($root, $path) = #_;
if ($^O =~ /Win32/) {
require Win32;
$root = Win32::GetLongPathName($root);
$path = Win32::GetLongPathName($path);
}
$root = dir($root);
$path = dir($path);
if ($root->subsumes($path)) {
return $path->relative($root);
} else {
print "\tFAIL:$root not in $path\n";
return "";
}
}
By the way, it's not very appropriate to handle the error there. The function should return an error signal (return undef, throw an exception, etc) and the caller should handle it as it sees fit. Separations of concerns.
You can also do it using local modifiers (perl extended regexes option):
sub relative_path
{
my ($root, $path) = #_;
my $pattern = "^\Q$root\E[\\\/](.*)$";
$pattern = "(?i)$pattern" if ($os eq "windows");
if ($path =~ /$pattern/)
{
return $1;
}
else
{
print "\tFAIL:$root not in $path\n";
}
}
(after I typed my answer I saw that Sinan also suggested it, but I decided to post my answer as well, since it gives a concreter answer to the question)