I am trying to list out the files which are only in directory ./a/b or ./a/d. Now I am explicitly specifying by using (-d && $_ =~ "b") || (-d && $_ =~ "d"). Is there any way I can put needed folders in an array?
use File::Find;
my $filename = "h*.txt";
print ("Now it's:", $filename);
find({
wanted => \&wanted,
preprocess => \&dir_preprocess,
}, './a');
sub dir_preprocess {
my (#entries) = #_;
#my #tmparr=("d","b"); This isn't working
if ( $File::Find::dir eq './a' ) {
#entries = grep { (-d && $_ =~ "b") || (-d && $_ =~ "d") }#entries;
}
return #entries;
}
my #mylist;
sub wanted{
if($_ =~ $filename) {
push(#mylist, $_);
}
}
print ("It's:", #mylist);
You can use | as "or" in a regex expression. (-d && $_ =~ /(b|d)/) would match either b or d. You could put the names in an array and then use join to generate the regex you need.
You might want to do things like prepend the path upto that depth, else you might get at different levels of the heirarchy. Also you can add $ at the end to indicate that it comes at the end.
Related
I need to decode logic from a database I reference when determining if an attribute is able to be associated with an item I am defining. The database uses standard logic flow and order of importance. I need to be able to determine nested conditional statements in the strings I pull from the database. For example:
$val = "(red && (blue || yellow)) && black";
The string I'd pull from the database would resemble:
$pull = "red.yellow.black";
I would separate $pull on . and store the values in an array, then I'd verify each value is in the array(s) I generate from the $val string. I am having trouble with the best method for determining how to unpack the nested logic though. Initially, I considered just using a regex, then removing the portion of the logic that has been assessed like so:
($eval) = $val =~ /.*\((.*)?\)/; $val =~ s/(.*)\((.*)?\)(.*)/$1 $2/;
If I do this in a while ($val =~ /\(/) loop, I could probably stack the extracted logic into arrays and evaluate the logical expression in each element to determine if each condition is true for the item I am evaluating, but there's something wrong with my regexp causing the sequence to fail. The ? is not as lazy as I thought, apparently...
my $val = "(red && (blue || yellow)) && black";
my $pull = "red.yellow.black";
my #stack = ();
until ($val !~ /\(/) {
my ($eval) = $val =~ /.*\((.*)?\)/;
push(#stack, $eval);
print "$eval\n";
$val =~ s/(.+)\((.*)?\)(.*)/$1 $2/;
print "$val\n";
}
If I just run the sequence in a perl shell, with some debugging info, I get this:
[bin]$ perl -e 'my $val = "((red && (blue || yellow) && black)"; my $pull = "red.yellow.black"; my #stack = (); until ($val !~ /\(/) { my ($eval) = $val =~ /.*\((.+)?\).?/; push(#stack, $eval); print "EVAL: $eval\n";$pause = <STDIN>;$val =~ s/(.*)\((.*)?\)(.*)/$1 $2/;print "VAL: $val\n"; }'
EVAL: blue || yellow) && black
VAL: ((red && blue || yellow) && black
EVAL: red && blue || yellow
VAL: ( red && blue || yellow
EVAL:
Any input on what I'm doing wrong would be appreciated, and any improvements on efficiency would be greatly appreciated! TIA
Update: Ok, so I just dumbed the whole thing down like this. I lose some of the order of operations, but if I evaluate each statement individually, whenever one of the conditions is broken, I'll pick it up and be able to act on the info accordingly. The code I am going to expand on is below. This would just return the individual components of the expression. I don't like how everything ouside parenthesis is concatenated into the first element, but I guess it still works fundamentally.
my $val = "(red && (blue || yellow)) && black";
$val =~ s/\s+//g;
my #chars = ();
my #ops = ();
while ($val) {
my ($char) = $val =~ /(.)/;
$val =~ s/.//;
push #chars, $char;
}
my $index = 0;
foreach my $char (#chars) {
if ($char =~ /\(/) {
$index++;
} elsif ($char =~ /\)/) {
$index--
}
#assign the character to the index.
$ops[$index] .= $char;
}
s/[()]//g for #ops;
print " #ops\n";
Output:
[/bin]$ perl decode_logic.pl
&&black red&& blue||yellow
It would be the real task to sadfsadf! Ghfgh% fgh65 %% of this text to replace the first 3 characters that are true for regexp, so replace it with 'o' sadfsadfoghfghofgh65o%
#!/usr/bin/perl -w
#list=<>;
chomp(#list);
foreach(#list) {
if($_ =~ m/\W/) {
# here is the problem because all the characters you find it
# overwrite it, but I only need to translate 3 characters from it
$_ =~ s/\W/o/g;
print $_."\n";
}
else {
print "->\n";
}
}
#start string => sadfsadf!ghfgh%fgh65%%
#result my program => sadfsadfoghfghofgh65oo
#and I need it => sadfsadfoghfghofgh65o%
change only the first 3 results to 'o'
Here is a way to go:
use feature 'say';
my $in = 'sadfsadf!ghfgh%fgh65%%';
$in =~ s/\W/o/ for 1..3;
say $in;
Output:
sadfsadfoghfghofgh65o%
Solved the problem
Code:
#!/usr/bin/perl -w
while ($be=<STDIN>)
{
chomp $be;
push (#list, $be);
}
foreach $a (#list)
{
$count=0;
if ($a=~/\W/)
{
while (($a=~m/\W/g) && ($count < 3))
{
$count++;
$a=~s/\W/o/;
}
print "$a\n";
}
else {print "->\n";}
}
your data in 'list' file
perl -pe 'for $i(1..3){s/\W/o/} ' list
on bash if you put data such list='sadfsadf!ghfgh%fgh65%%'
for((i=1;i<=3;i++)){ list=`echo $list|sed -E "s/\W/Y/"`; }
I am sure this can be done with split(), but I am more interested in doing it with s// if possible. I want to compare a supplied IP address with an array of IP addresses and find a match if existing. I also want to consider a partial match successful only if the entire element (not a substring of the array element) is a match.
For example: Supplied IP: 10.12.13.14
If the current array element is 10.12. or 10. or 10.12.13. We can consider that a match, but not 10.12.11.
This is to find if a given IP exists in the hosts.allow TCP wrappers file on a Linux host. I will add functionality to append the address if it is not covered in the file. Since Partial subnet matches like 10.120. or 192.168. work, I need to test for those as well. That is the code I am missing below where the placeholder "OR SUBSTRING MATCHES" exists. I want to know if my $IP = "1.2.3.4"; how do I make substring variables so I can perform a string comparison on "1.2.3." and "1.2." ?
#PSEUDO CODE EXAMPLE
my #IPS = (10.12.13.14, 191.168.1.2, 10.8., 172.16. );
my $IP = "10.8.3.44";
foreach (#IPS) { if( $IP eq $_ || split(/\d+\./, 1-3, $IP) eq $_ ) { print $IP matches current IP: $_\n}
# That split is supposed to represent "10." "10.8." and "10.8.3." That is the logic I am trying to accomplish, but I would like to use s// if it fits the job, otherwise I am open to split() or other suggestions
#REAL CODE EXAMPLE
#!/usr/bin/perl
my $IP = $ARGV[0];
my $FILE = '/etc/hosts.allow';
# Make sure it is an IP with either 157. or 140. as first octet
unless ( $IP =~ qr/(140|157)\.(\d{1,3}\.){2}\d{1,3}/ ) {
die "Usage: $0 IP Address" } else {
open (FH, "<", "$FILE");
foreach $LINE (<FH>) {
if ( $LINE =~ qr/^sshd: (.*)/i ) {
#LIST = split(", ", $1);
foreach (#LIST) {
chomp $_;
if($IP eq $_) || (OR SUBSTRING MATCHES ) <-need code here {
print "IP ADDRESS: $IP found! \n";
} else { print "$_ is not a match\n"};
}
}
}
}
Why reinvent the wheel?
use strict;
use warnings;
use feature qw/say/;
use Net::Subnet;
my $allowed_hosts = subnet_matcher qw(
10.8.0.0/16
10.12.13.14/32
191.168.1.2/32
172.16.0.0/16
);
for my $ip (qw/10.8.3.44/) {
if ($allowed_hosts->($ip)) {
say "$ip is allowed!";
}
else {
say "$ip is disallowed!";
}
}
You can build a regular expression to match against your accepted list. As M42 already demonstrated, you need to use quotemeta so that your period's aren't treated as the any character. You also need to be careful about your boundary conditions as well:
my #ips = qw(10.12.13.14 191.168.1.2 10.8. 172.16.);
my $ips_list = join '|', map {/\d$/ ? "$_\$" : $_} map quotemeta, #ips;
my $ips_re = qr{^(?:$ips_list)};
while (<DATA>) {
chomp;
if ($_ =~ $ips_re) {
print "(pass) $_\n";
} else {
print "(fail) $_\n";
}
}
__DATA__
10.8.3.44
999.10.8.999
10.12.13.14999
10.12.13.14
172.16.99.99
191.168.1.2
191.168.1.29
Outputs:
(pass) 10.8.3.44
(fail) 999.10.8.999
(fail) 10.12.13.14999
(pass) 10.12.13.14
(pass) 172.16.99.99
(pass) 191.168.1.2
(fail) 191.168.1.29
How about:
if ( ($IP eq $_) || ($IP =~ /^\Q$_/) ) {
I am taking the user input via -f option, and whatever he enters, accordingly files are being searched recursively.
My problem is: When user enters "tmp*", then also it searches for "abctmp", "xyztmp" etc. What I want to do is, only files starting with tmp should come.
In short, whatever user enters accordingly files should be pushed to array.
Currently I am doing this, but I am sure there's some classy, short way to do it.
#! /perl/bin/perl
use strict;
use warnings;
use File::Find;
use getopt::Long;
my $filename="tmp*.txt";
find( { wanted => \&wanted,
preprocess => \&dir_search,
}, '.');
sub wanted{
my $regex;
my $myop;
my #mylist;
my $firstchar= substr($filename, 0,1); # I am checking first character.
# Whether it's ".*tmp" or just "tmp*"
if($filename=~ m/[^a-zA-Z0-9_]/g){ #If contain wildcard
if($firstchar eq "."){ # first character "."
my $myop = substr($filename, 1,1);
my $frag = substr($filename,2);
$filename = $frag;
$regex = '\b(\w' . ${myop}. ${filename}. '\w*)\b';
# Has to find whatever comes before 'tmp', too
} else {
$regex = '\b(' . ${myop}. ${filename}. '\w*)\b';
# Like, "tmp+.txt" Only search for patterns starting with tmp
}
if($_ =~ /$regex/) {
push(#mylist, $_);
}
} else {
if($_ eq $filename) { #If no wildcard, match the exact name only.
push(#mylist, $_);
}
}
}
sub dir_search {
my (#entries) = #_;
if ($File::Find::dir eq './a') {
#entries = grep { ((-d && $_ eq 'g') ||
((-d && $_ eq 'h') ||
(!(-d && $_ eq 'x')))) } #entries;
# Want from 'g' and 'h' folders only, not from 'x' folder
}
return #entries;
}
And another thing is, I want to search for only '.txt' files. Where should I put that condition?
#!/perl/bin/perl
sub rec_dir {
($dir,$tmpfile_ref) = #_;
opendir(CURRENT, $dir);
#files = readdir(CURRENT);
closedir(CURRENT);
foreach $file (#files) {
if( $file eq ".." || $file eq "." ) { next; }
if( -d $dir."/".$file ) { rec_dir($dir."/".$file,$tmpfile_ref); }
elsif( $file =~ /^tmp/ && $file =~ /\.txf$/ ) { push(#{$tmpfile_ref},$dir."/".$file); }
}
}
#matching_files = ();
$start_dir = ".";
rec_dir($start_dir,\#matching_files);
foreach $file (#matching_files) { print($file."\n"); }
I didn't test it. Barring typographical errors I think it will work.
Perl's xcopy has the method fn_pat to specify a regular expression for the pattern matching and I want to use this to recursively copy a directory ignoring all files/folders that any of these strings:
.svn
build
test.blah
I am stumbling with the syntax to do that, I have looked over many perl regular expression guides but for the life of me I just can not get the hang of it. I appreciate any help.
Thanks.
... update ...
I found a perl regex that seems to be working, just not with xcopy's fn_pat. Not sure if this is a bug with xcopy or if my expression is not correct, however my tests show its ok.
$exp = '^(?!.*(\.svn|build|test\.blah)).*$';
if( '/dev/bite/me/.svn' =~ $exp ){ print "A\n"; }
if( '/dev/bite/me/.svn/crumbs' =~ $exp ){ print "B\n"; }
if( '/dev/build/blah.ext' =~ $exp ){ print "C\n"; }
if( '/dev/crap/test.blah/bites' =~ $exp ){ print "D\n"; }
if( '/dev/whats/up.h' =~ $exp ){ print "E\n"; }
only E prints as I was hoping. I'm curious to know if this is correct or not as well as to any ideas why its not working with xcopy.
Here is where File::Xcopy calls File::Find::finddepth:
sub find_files {
my $self = shift;
my $cls = ref($self)||$self;
my ($dir, $re) = #_;
my $ar = bless [], $cls;
my $sub = sub {
(/$re/)
&& (push #{$ar}, {file=>$_, pdir=>$File::Find::dir,
path=>$File::Find::name});
};
finddepth($sub, $dir);
return $ar;
}
Here $re is your regexp.
According to the File::Find docs, $_ will be set to just the leaf name of the file being visited unless the no_chdir option used.
The only way I can see to get the no_chdir option passed to finddepth is to monkey-patch File::Xcopy::finddepth:
use File::Xcopy;
*{"File::Xcopy::finddepth"} = sub {
my ($sub, $dir) = #_;
File::Find::finddepth({ no_chdir => 1, wanted => $sub}, $dir);
};