I want to change a perl script that executes a loop some times, and I want to pass the number of loops by command line option. The program now receives some options, then I need to change it to receive a new parameter, but it is the first time I see a perl script, then I don't know how to change.
The start of program (to parse command line options) is:
if ($#ARGV >= 1) {
for ($i = 1; $i <= $#ARGV; $i++) {
if ($ARGV[$i] =~ /^\-/) {
if ($ARGV[$i] =~ /\-test/) {
//do something
}
} else {
//do something other
}
}
}
I think that I must put something like:
if ($ARGV[$i] =~ /^\-L40/)
But it only match to 40, I don't know how to parse the number attached to the -L parameter to use for the loop limit.
Thanks in advance and sorry if there is any similar question, but I don't find any.
use Getopt::Long qw( );
sub usage {
print(STDERR "usage: prog [--test] [-L NUM]\n");
exit(1);
}
GetOptions(
'test' => \my $opt_test,
'L=i' => \my $opt_L,
)
or usage();
die("-L must be followed by a positive integer\n")
if defined($opt_L) && $opt_L < 1;
Something like:
my $loopLimit = 1; # default
if ($#ARGV >= 1)
{
for ($i = 1; $i <= $#ARGV; $i++)
{
if ($ARGV[$i] =~ /^\-/)
{
if ($ARGV[$i] =~ /\-test/)
{
# do something
}
elsif ($ARGV[$i] =~ /\-L(\d+)/) # -L followed by digits
{
$loopLimit = $1;
}
}
else
{
# do something other
}
}
}
Related
I trying to get my hands dirty with PHPSpec.
Basically Im trying to make a TimeSpan as a practice.
I want to make sure that all possible valid inputs are accepted.
function it_can_be_created_from_24_format()
{
$hours = 24;
$minutes = 60;
$seconds = 60;
for ($h = 1; $h < $hours; $h++) {
for ($m = 1; $m < $minutes; $m++) {
for ($s = 1; $s < $seconds; $s++) {
$this->beConstructedThrough('fromString', [$this->buildTimeString($h, $m, $s)]);
$this->shouldNotThrow(\InvalidArgumentException::class)->duringInstantiation();
$this->getHours()->shouldBe($h);
$this->getMinutes()->shouldBe($m);
$this->getSeconds()->shouldBe($s);
}
}
}
}
private function buildTimeString($h, $m, $s)
{
$minutes = ($m < 9) ? '0'.$m : $m;
$seconds = ($s < 9) ? '0'.$s : $s;
return sprintf('%s:%s:%s', $h, $minutes, $seconds);
}
But im receiving this error:
you can not change object construction method when it is already instantiated
I have to set 3 variables depending on the IP address.
I discovered that I can use switch with -regex, but I don't know how to check if address is between two addresses.
$ip = (get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}).ipaddress[0]
switch -regex ($ip)
{
"address 192.168.0.1-192.168.0.255" { $val = 3; }
"address 192.168.1.1-192.168.1.100" { $val = 1; }
"address 192.168.1.101-192.168.1.200" { $val = 4; }
"address 192.168.1.201-192.168.1.255" { $val = 5; }
default { exit }
}
I don't think regex is the best way of handling this. I'd probably do something like this
$ip = (get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}).ipaddress.split('.')
switch ($ip)
{
{$ip[2] -eq 0} { $val = 3; } #match anything in 192.168.0.1-255
{$ip[3] -in 1..100} { $val = 1; }
{$ip[3] -in 101..200} { $val = 4; }
{$ip[3] -in 201..255} { $val = 5; }
default { exit }
}
$val
If your IP blocks are different than what was provided in the example it would just be a matter of adjusting the switch conditions to match the appropriate octets
I have a awk based splitter that splits a huge file based on regex. But the problem is that I am getting a makes too many files error. Even i have a conditional close. If you could help me figure out what I am doing wrong I would be much grateful.
awk 'BEGIN { system("mkdir -p splitted/sub"++j) }
/<doc/{x="F"++i".xml";}{
if (i%5==0 ){
++i;
close("splitted/sub"j"/"x);
system("mkdir -p splitted/sub"++j"/");
}
else{
print > ("splitted/sub"j"/"x);
}
}' wiki_parsed.xml
The simple answer is that close isn't being called often enough. Here's an illustrative example of why:
Using an input file like:
<doc somestuff
another line
yet another line
<doc the second
still more data
<doc the third
<doc the fourth
<doc the fifth
I can make an executable awk file based on your script like:
#!/usr/bin/awk -f
BEGIN { system_(++j) }
/<doc/{x=++i}
{
if (i%5==0 ){ ++i; close_(j"/"x); system_(++j) }
else{ open_(j"/"x) }
}
function call_f(funcname, arg) { print funcname"("arg")" }
function system_(cnt) { call_f( "system", cnt ) }
function open_(f) { if( !(f in a) ) { call_f( "open", f ); a[f]++ } }
function close_(f) { call_f( "close", f ) }
which if I put into a file called awko can be run like awko data to produce the following:
system(1)
open(1/1)
open(1/2)
open(1/3)
open(1/4)
close(1/5)
system(2)
The script I made is just indicating how many times you're calling each function by shadowing a real function call with a local function with a trailing _. Notice how many times open() is printed compared to close() for the same arguments. Also, I ended up renaming print > to open_ just to illustrated that it's what's opening the files( once per file name ).
If I change the executable awk file to the following, you can see close being called enough:
#!/usr/bin/awk -f
BEGIN { system_(++j) }
/<doc/{ close_(j"/"x); x=++i } # close_() call is moved to here.
{
if (i%5==0 ){ ++i; system_(++j) }
else{ open_(j"/"x) }
}
function call_f(funcname, arg) { print funcname"("arg")" }
function system_(cnt) { call_f( "system", cnt ) }
function open_(f) { if( !(f in a) ) { call_f( "open", f ); a[f]++ } }
function close_(f) { call_f( "close", f ) }
which gives the following output:
system(1)
close(1/)
open(1/1)
close(1/1)
open(1/2)
close(1/2)
open(1/3)
close(1/3)
open(1/4)
close(1/4)
system(2)
where it should be clear that close() is being called one more time than enough. The first time it's being called on a file that doesn't exist. With a true close() call, the fact that such a file has never been printed should just be ignored and no actual close will be attempted. In each other case, the last open() matches a close() call.
Moving your close() call in your script as in the second example script should fix your error.
This is what i got it to be working perfectly
awk 'BEGIN { system("mkdir -p splitted/sub"++j) }
/<doc/{x="F"++i".xml";}{
if (i%1995==0 ){
++i;
system("mkdir -p splitted/sub"++j"/");
}
else{
print >> ("splitted/sub"j"/"x);
close("splitted/sub"j"/"x);
}
}' wiki_parsed.xml
I got this kind of CSV file:
name,x-extension,value,extra
"Roger","9890","",""
"Nicole","9811","president, ceo",""
...
Now, I want the find the maximum size of each field in the file. So I used this awk script:
Updated script:
NR==1 {
gsub(/\r/,"",$0) #remove linefeed from last field name
for (n = 1; n <= NF; n++) {
colname[n]=$n;
maxlen[n]=-1;
}
nbrField = NF; # will get bump +2 by the new FS
FS="\",\"|^\"|\"$";
}
NR>1 {
for (n = 2; n <= nbrField+1; n++) {
if (length($n)>maxlen[n-1]) {
maxlen[n-1]=length($n);
}
}
}
END {
for(i = 1; i <= nbrField; i++) {
printf "%s : %s\n", colname[i], maxlen[i]
}
}
The problem a got is I need to change the field separator AFTER reading the first line because as you can see, the header don't use double quote for field delimiter and there is coma INSIDE some field.
I tried to play with this -F option on my awk command line but I can't find the right combination of regex to do the trick..
> awk -F'", "|^"|"$' -f myprog mydata ==>(don't work)
Help! :-)
Change FS in the block that processes the first line:
NR==1 {
for(n = 1; n <= NF; n++) {
colname[n]=$n
}
FS="\",\"|^\"|\"$"
}
I prefer to use a real CSV parser for CSV data. For example, Perl:
perl -MText::CSV -MList::Util=max -nE '
BEGIN {$csv = Text::CSV->new({binary=>1})}
$csv->parse($_);
#row = $csv->fields();
if ($. == 1) {
#h = #row; $n = $#row;
}
else {
$max{$h[$_]} = max($max{$h[$_]}, length $row[$_]) for (0..$n)
}
END {
while (($k,$v) = each %max) {say join ":", $k, $v}
}
' << DATA
name,x-extension,value,extra
"Roger","9890","",""
"Nicole","9811","president, ceo",""
DATA
value:14
name:6
extra:0
x-extension:4
I'm writing a compiler, and I have working code for handling infinitely nested if statements, but it's kind of a hack. I don't know if it's safe to do this?
con_statement:
IF exp DO
{
$1 = ifNum++;
if($2 == BOOLEAN_TYPE || $2 == INTEGER_TYPE)
{
utstring_printf(code, "\tpop\teax\n");
utstring_printf(code, "\tcmp\teax, 0\n");
utstring_printf(code, "\tje\tIF_END_%d\n", $1);
}
if($2 == FLOAT_TYPE)
{
utstring_printf(code, "\tnop\n");
}
}
program FI
{
utstring_printf(code, "IF_END_%d:\n", $1);
}
;
This works fine but it would be IMO clearer to use $$/$4:
con_statement:
IF exp DO
{
$$ = ifNum++;
if($2 == BOOLEAN_TYPE || $2 == INTEGER_TYPE)
{
utstring_printf(code, "\tpop\teax\n");
utstring_printf(code, "\tcmp\teax, 0\n");
utstring_printf(code, "\tje\tIF_END_%d\n", $$);
}
if($2 == FLOAT_TYPE)
{
utstring_printf(code, "\tnop\n");
}
}
program FI
{
utstring_printf(code, "IF_END_%d:\n", $4);
}
;
The first action is generating a value (which it puts into $$), and then later actions can access that value.
Alternately (and particularly if you want to support ELSE), it may make sense to split this initial action onto a separate production:
con_statement:
if_head program FI
{ utstring_printf(code, "IF_FALSE_%d:\n", $1); }
| if_head program ELSE
{ utstring_printf(code, "\tjmp\tIF_END_%d\n", $1);
utstring_printf(code, "IF_FALSE_%d:\n", $1); }
program FI
{ utstring_printf(code, "IF_END_%d:\n", $1); }
;
if_head:
IF exp DO
{ $$ = ifNum++;
:
;
This allows using the same action for plain if and if/else, avoiding a grammar conflict, since at the point you are parsing the IF..DO you don't know whether there will be an ELSE or not.