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)
Related
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))
}
}
Using Perl I would like to check if the two lines highlighted below exist in a text file . Each line is preceded by a tab.
CF=CFU-ALL-PROV-NONE-YES-NO-NONE-YES;
CF=CFB-ALL-PROV-NONE-YES-YES-NONE-YES;
***CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES;***
CF=CFNRY-ALL-PROV-NONE-YES-YES-NONE-YES;
CF=CFNRC-ALL-PROV-NONE-YES-NO-NONE-YES;
***CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES;***
CF=CFD-TS10-REG-9124445544-YES-YES;
I am using the following if statement but it is not matched
if (/\t*CF=(CFU-TS10-ACT-(NONE|\d+))/ && /\t*CF=(CFB-TS10-ACT-(NONE|\d+))/)
{
say "this case is found here .....";
}
What am I doing wrong ?
Edited
This is the program I wrote :-
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my $HSSIN='D:\testproject\HSS-export-test-run-small.txt';
my $ofile = 'D:\testproject\HSS-output.txt';
open (INFILE, $HSSIN) or die "Can't open input file";
open (OUTFILE,"> $ofile" ) or die "Cant open file";
my $add;
my $MSISDN;
my $line;
sub callForwardingsCF()
{
if (/\t*CF=(CFU-TS10-ACT-(NONE|\d+))/ && /\t*CF=(CFB-TS10-ACT-(NONE|+\d+))/)
{
say "this case is found here .....";
}
} # end sub callForwardingsCFD
while (<INFILE>)
{
if (/<SUBEND/)
{
say "SUBEND found";
#$line = $1 if /^\s*MSISDN=(\d+);/;
print OUTFILE "processSingle UpdateCommand GSUB MKEY $line";
print OUTFILE "\n";
}
if ($_ =~ /^\t*MSISDN=(\d+);/)
{ #find MSISDN in file global search
say "STARTER MSISDN is $1";
$MSISDN = $1;
$add = $1;
$line = "$1"; #group 1
}
callForwardingsCF(); #callForwardings
}
close INFILE;
close OUTFILE;
Example of a record in the input file
<BEGINFILE>
<SUBBEGIN
IMSI=232191400029053;
MSISDN=4369050064401;
DEFCALL=TS11;
CURRENTNAM=BOTH;
CAT=COMMON;
TBS=TS11&TS12&TS21&TS22;
VLRLIST=10;
SGSNLIST=10;
SMDP=MSC;
CB=BAOC-ALL-PROV;
CB=BOIC-ALL-PROV;
CB=BOICEXHC-ALL-PROV;
CB=BICROAM-ALL-PROV;
CW=CW-ALL-PROV;
CF=CFU-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFB-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES-65535-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFNRY-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFNRC-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES-65535-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFD-TS10-REG-91436903000-YES-YES-25-YES-65535-YES-YES-NO-NO-NO-YES-YES-YES-YES-NO;
TCSISTATE=YES;
OCSISTATE=YES;
CONTROL=SUB;
WPA=0;
GS=HOLD&MPTY&ECT&CLIR&CLIP;
CLIRES=TEMPALLOW;
CLIPOC=NO;
OCSI=10;
CFSMS=ACT-10-914366488325207-YES-YES-NO-NO-NO;
ARD=PROV;
SUBRES=ALLPLMN;
IST_ALERT_TIMER=120;
IST_ALERT_RESPONSE=2;
SUB_AGE=0;
MIMSI=240076400029053-ONELIVE-2-2-1-0-0;
MIMSI=232191400029053-ONELIVE-1-1-1-0-0;
SID=2805158185721065;
MCSISTATE=YES;
CLRBSG=CLIP-YES-NO-NO-NO-NO;
UPLCSLCK=NO;
UPLPSLCK=NO;
DEFOFAID=10;
EPS_PROFILE_ID=1;
TGPPAMBRMAXUL=50000000;
TGPPAMBRMAXDL=150000000;
ARD_EXT=NULL-NULL-NULL-N3GPPNOTALLOWED;
FRAUDTPL_ID=10;
HLR_INDEX=1;
LTEAUTOPROV=NO;
PSSER=1-1-10-1-NONE-DYNAMIC-00000000;
EPSSER=1-10-10-1-NONE-DYNAMIC-00000000-1;
MPS=NO;
<SUBEND
Thanks,
Graham
Per default regexes match linewise.
So if you were trying to match an input that contains multiple lines, you would have to use one of the modifiers that allows the regex to match the entire string.
See the the perl regex documentation - the chapter "Modifiers".
Then you should add the s modifiler and change your if statement to:
if ( /\t*CF=(CFB-TS10-ACT-(NONE|\d+))/s &&
/\t*CF=(CFU-TS10-ACT-(NONE|\d+))/s ) {
say "found";
}
If you read linewise you will never have both of your regexes match for the same line, so you would need to do your regexes seperately as already suggested by the other answer.
#$/ = ""; #without paragraph mode
open my $file, '<', 'data_file';
binmode $file;
while(<$file>){
print $_ if ( $_ =~ /\s+CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;/ ||
$_ =~ /\s+CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;/ );
}
EDIT:
OR, you can do it in paragraph mode if conditions allow it.
$/ = "";
open my $file, '<', 'data_file';
binmode $file;
while(<$file>){
(undef, $first) = split (/\s+(CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;)/, $_);
(undef, $second) = split(/\s+(CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;)/, $_ );
print $first . "\n" . $second;
}
Code is tested and seems to work fine with supplied data.
Also, those are not tabs "\t" ... those are spaces "\s+" preceding those lines. Best thing is to learn your data set before you try to parse it ;)
Typically perl processes file "line by line".
Try something like sample script below:
my($line1,$line2);
while(<STDIN>) {
$line1=$_ if /\t*CF=(CFU-TS10-ACT-(NONE|\d+))/
$line2=$_ if /\t*CF=(CFB-TS10-ACT-(NONE|\d+))/
if( $line1 and $line2 ) {
say "this case is found here .....";
last; # skip processing remaning lines
}
}
Alternatively you may "slurp" whole file into one scalar variable.
Now suppose say i have this line in a file:
my %address = (
or any such similar line in which i have defined the hash.
I want to find the character "(" in the line and store "address" in say $hash_name. How do I do it?
Basic idea is to capture the name of the hash defined in the files.
I am trying to do is,
foreach $line <MYFILE> {
if($line =~ /($/ {
How do I proceed further?
Not sure if I understood your problem, but, how about:
my %hash;
while (my $line = <MYFILE>) {
if ($line =~ /\%(\w+)\s*=\s*\($/) {
$hash{$1} = 1;
}
}
open (F1,"inputfile.txt") or die("unable to open inputfile.txt");
my $hash_name
while (<F1>) {
if (/%(\w+) *= *\(/) {
$hash_name = $1;
print $hash_name;
}
}
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}
Supposed I have a file with Perl-code: does somebody know, if there is a module which could find the closing "}" of a certain subroutine in that file.
For example:
#!/usr/bin/env perl
use warnings;
use 5.012;
routine_one( '{°^°}' );
routine_two();
sub routine_one {
my $arg = shift;
if ( $arg =~ /}\z/ ) {
say "Hello my }";
}
}
sub routine_two {
say '...' for 0 .. 10
}
The module should be able to remove the whole routine_one or it should can tell me the line-number of the closing "}" from that routine.
You want to use PPI if you are going to be parsing Perl code.
#!/usr/bin/env perl
use warnings;
use 5.012;
use PPI;
my $file = 'Example.pm';
my $doc = PPI::Document->new( $file );
$doc->prune( 'PPI::Token::Pod' );
$doc->prune( 'PPI::Token::Comment' );
my $subs = $doc->find( sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name eq 'layout' } );
die if #$subs != 1;
my $new = PPI::Document->new( \qq(sub layout {\n say "my new layout_code";\n}) );
my $subs_new = $new->find( sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name eq 'layout' } );
$subs->[0]->block->insert_before( $subs_new->[0]->block ) or die $!;
$subs->[0]->block->remove or die $!;
# $subs->[0]->replace( $subs_new->[0] );
# The ->replace method has not yet been implemented at /usr/local/lib/perl5/site_perl/5.12.2/PPI/Element.pm line 743.
$doc->save( $file ) or die $!;
The following will work in case your subroutines don't contain any blank lines, like the one in your example:
#!/usr/bin/perl -w
use strict;
$^I = ".bkp"; # to create a backup file
{
local $/ = ""; # one paragraph constitutes one record
while (<>) {
unless (/^sub routine_one \{.+\}\s+$/s) { # 's' => '.' will also match "\n"
print;
}
}
}