I'm new to threads. I written a script to check the threads module in which i have some modules which do some pattern matching job and returns to two values 1st the input and second match found instance along with its line number. But using the threads it returns nothing.
here is the code
use threads;
sub pattern_finder($){
my $filebuf = shift;
my #threads;
my $pattern_found;
my $thr1 = threads->create(\&sub_pattern1,$filebuf);
push(#threads, $thr1);
my $thr2 = threads->create(\&sub_pattern2,$filebuf);
push(#threads, $thr2);
my $thr3 = threads->create(\&sub_pattern3,$filebuf);
push(#threads, $thr3);
for my $t(#threads){
($filebuf, $tmp)= $t->join();
$pattern_found .= $tmp;
}
return $filebuf, $pattern_found;
}
sub sub_pattern1($)
{
my ($filebuf) = shift;
my $found;
while($filebuf =~ /<LINE(\d+)>Pattern1/gsi)
{
$found .= "Pattern1 found at line $1\n";
}
return $filebuf, $found;
}
sub sub_pattern2($)
{
my ($filebuf) = shift;
my $found;
while($filebuf =~ /<LINE(\d+)>Pattern2/gsi)
{
$found .= "Pattern2 found at line $1\n";
}
return $filebuf, $found;
}
sub sub_pattern3($)
{
my ($filebuf) = shift;
my $found;
while($filebuf =~ /<LINE(\d+)>Pattern3/gsi)
{
$found .= "Pattern3 found at line $1\n";
}
$found = "$pre_checks"."$found";
return $filebuf, $found;
}
Can anyone suggest me what is wrong in the code?
Ah yes, a common problem: The code reference you pass to threads->create is called in the context that the create method was called. Here, this is scalar context. Thus return $filebuf, $found is equivalent to return $found.
As you use that single element in list context when joining the thread, you do something equivalent to $filebuf = $t->join, $tmp = undef which is not what you're expecting.
This can be fixed in two ways:
create the thread in list context:
my ($thr1) = threads->create(...)
explicitly specify the context:
my $thr1 = threads->create({context => 'list'}, \&sub_pattern1, $filebuf);
See also the section on context in the threads documentation.
Note also that there is no reason to return $filebuf at all as you don't really use that value – you just return the $filebuf of the last thread which was joined.
Tip: If you're using multithreading for performance, you should benchmark your code both with and without threads – in your case the non-threaded (sequential) solution is likely to perform better.
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))
}
}
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}
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)
Advance New Year Wishes to All.
I have an error log file with the contents in a pattern parameter, result and stderr (stderr can be in multiple lines).
$cat error_log
<parameter>:test_tot_count
<result>:1
<stderr>:Expected "test_tot_count=2" and the actual value is 3
test_tot_count = 3
<parameter>:test_one_count
<result>:0
<stderr>:Expected "test_one_count=2" and the actual value is 0
test_one_count = 0
<parameter>:test_two_count
<result>:4
<stderr>:Expected "test_two_count=2" and the actual value is 4
test_two_count = 4
...
I need to write a function in Perl to store each parameters, result and stderr in an array or hash table.
This is our own internally defined structure. I wrote the Perl function like this. Is there a better way of doing this using regular expression itself?
my $err_msg = "";
while (<ERR_LOG>)
{
if (/<parameter>:/)
{
s/<parameter>://;
push #parameter, $_;
}
elsif (/<result>:/)
{
s/<result>://;
push #result, $_;
}
elsif (/<stderr>:/)
{
if (length($err_msg) > 0)
{
push #stderr, $err_msg;
}
s/<stderr>://;
$err_msg = $_;
}
else
{
$err_msg .= $_;
}
}
if (length($err_msg) > 0)
{
push #stderr, $err_msg;
}
If you're using Perl 5.10, you can do something very similar to what you have now but with a much nicer layout by using the given/when structure:
use 5.010;
while (<ERR_LOG>) {
chomp;
given ($_) {
when ( m{^<parameter>: (.*)}x ) { push #parameter, $1 }
when ( m{^<result>: (.*)}x ) { push #result, $1 }
when ( m{^<stderr>: (.*)}x ) { push #stderr, $1 }
default { $stderr[-1] .= "\n$_" }
}
}
It's worth noting that for the default case here, rather than keeping a separate $err_msg variable, I'm simply pushing onto #stderr when I see a stderr tag, and appending to the last item of the #stderr array if I see a continuation line. I'm adding a newline when I see continuation lines, since I assume you want them preserved.
Despite the above code looking quite elegant, I'm not really all that fond of keeping three separate arrays, since it will presumably cause you headaches if things get out of sync, and because if you want to add more fields in the future you'll end up with lots and lots of variables floating around that you'll need to keep track of. I'd suggest storing each record inside a hash, and then keeping an array of records:
use 5.010;
my #records;
my $prev_key;
while (<ERR_LOG>) {
chomp;
given ($_) {
when ( m{^<parameter> }x ) { push(#records, {}); continue; }
when ( m{^<(\w+)>: (.*)}x ) { $records[-1]{$1} = $2; $prev_key = $1; }
default { $records[-1]{$prev_key} .= "\n$_"; }
}
}
Here we're pushing a new record onto the array when we see a field, adding an entry to our hash whenever we see a key/value pair, and appending to the last field we added to if we see a continuation line. The end result of #records looks like this:
(
{
parameter => 'test_one_count',
result => 0,
stderr => qq{Expected "test_one_count=2" and the actual value is 0\ntest_one_count=0},
},
{
parameter => 'test_two_count',
result => 4,
stderr => qq{Expected "test_two_count=2" and the actual value is 4\ntest_two_count=4},
}
)
Now you can pass just a single data structure around which contains all of your records, and you can add more fields in the future (even multi-line ones) and they'll be correctly handled.
If you're not using Perl 5.10, then this may be a good excuse to upgrade. If not, you can translate the given/when structures into more traditional if/elsif/else structures, but they lose much of their beauty in the conversion.
Paul
The main thing that jumps out for refactoring is the repetition in the matching, stripping, and storing. Something like this (untested) code is more concise:
my( $err_msg , %data );
while (<ERR_LOG>) {
if(( my $key ) = $_ =~ s/^<(parameter|result|stderr)>:// ) {
if( $key eq 'stderr' ) {
push #{ $data{$key} } , $err_msg if $err_msg;
$err_msg = $_;
}
else { push #{ $data{$key} } , $_ }
}
else { $err_msg .= $_ }
}
# grab the last err_msg out of the hopper
push #{ $data{stderr} } , $err_msg;
... but it may be harder to understand six months from now... 8^)
Looks nice. =) An improvement is probably to anchor those tags at the beginning of the line:
if (/^<parameter>:/)
It'll make the script a bit more robust.
You can also avoid the stripping of the tag if you catch what's after it and use only that part:
if (/^<parameter>:(.*)/s)
{
push #parameter, $1;
}