I was looking for an example to limit the number of forked processes to run at the same time and I ran across this old code
#!/usr/bin/perl
#total forks, max childs, what to run
#function takes 2 scalars and a reference to code to run
sub mfork ($$&) {
my ($count, $max, $code) = #_;
# total number of processes to spawn
foreach my $c (1 .. $count) {
#what is happening here? why wait vs waitpid?
wait unless $c <= $max;
die "Fork failed: $!\n" unless defined (my $pid = fork);
# i don't undestand the arrow notation here and how it calls a function,
#also unless $pid is saying run function unless you're the parent
exit $code -> ($c) unless $pid;
}
#no idea what's happening here, why are we waiting twice? for the last process?
#why 1 until (-1 == wait)? what's 1 doing here
1 until -1 == wait;
}
#code to run
mfork 10, 3, sub {
print "$$: " . localtime() . ": Starting\n";
select undef, undef, undef, rand 2;
print "$$: " . localtime() . ": Exiting\n";
};
Let's take a look at the code. Code is yours, with most of your comment removed. All other comments are mine.
#!/usr/bin/perl
# total forks, max childs, what to run
# function takes 2 scalars and a reference to code to run
sub mfork ($$&) {
my ($count, $max, $code) = #_;
# total number of processes to spawn
foreach my $c (1 .. $count) {
# wait waits for any child to return,
# waitpid for a specific one
wait unless $c <= $max;
die "Fork failed: $!\n" unless defined (my $pid = fork);
# the arrow is used to call the coderef in $code
# and the argument is $c. It's confusing because it has
# the space. It's a deref arrow, but looks like OOp.
# You're right about the 'unless $pid' part.
# If there is $pid it's in the parent, so it does
# nothing. If it is the child, it will run the
# code and exit.
exit $code -> ($c) unless $pid;
}
# This is reached after the parent is done with the foreach.
# It will wait in the first line of the foreach while there are
# still $count tasks remaining. Once it has spawned all of those
# (some finish and exit and make room for new ones inside the
# loop) it gets here, where it waits for the remaining ones.
# wait will return -1 when there are no more children.
# The '1 until' is just short for having an until loop that
# doesn't have the block. The 1; is not a costly operation.
# When wait == -1 it passes the line, returning from the sub.
1 until -1 == wait;
}
# because of the prototype above there are no () needed here
mfork 10, 3, sub {
print "$$: " . localtime() . ": Starting\n";
select undef, undef, undef, rand 2;
print "$$: " . localtime() . ": Exiting\n";
};
Let's look at stuff in detail.
There is wait and waitpid. wait will wait until any of the children returns. That is useful because the program doesn't care which slot gets freed. As soon as one finishes, a new one can be spawned. waitpid takes an argument of a specific $pid. That's not helpful here.
The $code->($c) syntax runs a coderef. Just like %{ $foo }{bar} will dereference a hashref, &{ $baz }() will dereference (and run, that's the ()) a coderef. An easier to read way is $foo->{bar}. Just the same is true for $baz->(). The arraow derefs it. See perlref and perlreftut.
While this is nice and useful, maybe it would make more sense to use Parallel::Forkmanager, which gives the power of this in a lot less lines of code, and you don't need to worry how it works.
use strict;
use warnings;
use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new(3); # max 3 at the same time
DATA_LOOP:
foreach my $data (1 .. 10) {
# Forks and returns the pid for the child:
my $pid = $pm->start and next DATA_LOOP;
... do some work with $data in the child process ...
print "$$: " . localtime() . ": Starting\n";
select undef, undef, undef, rand 2;
print "$$: " . localtime() . ": Exiting\n";
$pm->finish; # Terminates the child process
}
That's it. Way clearer to read. :)
Related
So i've made functions to capture pipes
sub capture_stdout (&) {
my $s;
open(local *STDOUT, '>', \$s);
shift->();
return $s;
}
sub capture_stderr (&) {
my $s;
open(local *STDERR, '>', \$s);
shift->();
return $s;
}
These work great. Now the challenge i am facing is, i want to make a function that takes the pipe(s) as arguments, and redirects all of them in a single sub.
I have yet been unsuccessful in making it work. So far I've cooked up something that compiles;
sub capture(#&) {
my $c = pop;
my $o = [];
say {$_[$_]} $_[$_] for (0 .. $#_);
open(local *{$_[$_]}, '>', \$o->[$_]) for (0 .. $#_);
$c->();
return $o;
}
use Data::Dumper;
say Dumper( capture *STDOUT, *STDERR, sub{ say 1; warn 2; } );
but it does not capture anything. I cant seem to figure out how to fix it. I'm convinced however that it is the local *{$_[$_]} that needs fixing, though i could be wrong.
The complete output is:
*main::STDOUT
*main::STDERR
1
2 at capture.pl line 15.
$VAR1 = [
undef,
undef
];
So then the question: Is it even possible to do what I'm attempting, and if so, how?
Thank you.
The problem with your code is that the effects of local are undone at the end of your
... for (0 .. $#_);
loop. By the time you call $c->(), the filehandles have their original values again.
So ...
You want to localize an arbitrary number of variables.
You can't use blocks (e.g. for (...) { ... }) because local is undone at the end of the scope it's in.
You can't use postfix for because apparently it implicitly creates its own mini-scope.
The solution? goto, of course!
(Or you could use recursion: Use a block, but never leave it or loop back. Just localize a single variable, then call yourself with the remaining variables. But goto is funnier.)
sub capture {
my $c = pop;
my $o = [];
my $i = 0;
LOOP: goto LOOP_END if $i >= #_;
local *{$_[$i++]};
goto LOOP;
LOOP_END:
open(*{$_[$_]}, '>', \$o->[$_]) or die "$_[$_]: $!" for 0 .. $#_;
$c->();
return $o;
}
Effectively we've created a loop without entering/leaving any scopes.
You need to actually switch out the file handles. To do that, first save the existing handles. Then create new ones that point into your output data structure. Once you've run the code, restore the original handles.
sub capture {
my $c = pop;
# we will keep the original handles in here to restore them later
my #old_handles;
my $o = [];
foreach my $i (0 .. $#_) {
# store the original handle
push #old_handles, $_[$i];
# create a new handle
open my $fh, '>', \$o->[$i] or die $!;
# stuff it into the handle slot of the typeglob associated with the old handle
*{$_[$i]} = $fh;
}
# run callback
$c->();
# restore the old handles
*{$_[$_]} = $old_handles[$_] for 0 .. $#_;
return $o;
}
SOLUTION:
The final product, not nearly as convoluted as the original goto loop:
=pod
=item C<capture>
capture takes a list of pipes/filehandles, a code block or sub, optionally arguments to send to
said block and returns any captured output as a string, or an array of strings.
my ($out, $err) = capture *STDOUT, *STDERR, sub { say 'faijas'; warn #_; }, 'jee';
my $output = capture *STDOUT, sub { say 'jee'; };
=cut
sub capture(#&;#) {
my (#o, #h);
# walk through #_, grab all filehandles and the code block into #h
push #h, shift while #_ && ref $h[$#h] ne 'CODE';
my $c = pop #h; # then separate the code block from #h, leaving only handles
# Really we want to do: open(local *{$_[$_]}, '>', \$o->[$_]) for (0 .. $#_);
# but because of scoping issues with the local keyword, we have to loop without
# creating an inner scope
my $i = 0;
R: open(local *{$h[$i]}, '>', \$o[$i]) or die "$h[$i]: $!" ;
goto R if ++$i <= $#h;
$c->(#_);
return wantarray ? #o : $o[0];
}
Big thanks to #melpomene and #simbabque for helping me around the initial problem, and #ikegami for pointing out oversights.
I wrote a simple script to manage the download time ( start and finish ) with wget in Linux-Gnu with Perl. There is no problem and everything works good, except I wish I could read a key from keyboard when the process is running.
I show a simple movement animation on the screen that I do not want to stop it and then read the key.
for example like mplayer or mpv that when you run it on the comman-line, you can press q to exit or s to take a picture from the screen.
A part of the script:
do {
system( "clear" );
($h, $m, $s) = k5mt::second_to_clock( $till_shutdown );
set_screen();
set_screen( 24 );
set_screen();
say "download was started ...";
set_screen();
set_screen( 24 );
set_screen();
printf "till finish: %02d %02d %02d\n", $h, $m, $s;
set_screen();
set_screen( 24 );
set_screen();
say "wget pid: [$wget_pid]";
set_screen();
set_screen( 24 );
set_screen();
$waiting_temp .= $animation[ $counter_animation++ ];
say $waiting_temp;
if( length( $waiting ) == $counter_animation ){
$waiting_temp = "";
$counter_animation = 0;
}
set_screen();
set_screen( 24 );
sleep 1;
$till_shutdown--;
} while( $till_shutdown );
the words waiting till finish..... is shown consecutively ( without interruption ) and I want to read a key like q to exit from the program.
UPDATE
I am looking for a solution with as many option as I want, if I have wanted just for exit from the program I simply tell the user to press Ctrl + C
Is it possible with scripting in Perl? or not? If it is, How?
NOTE: if it is possible without any modules please say the solution
without any module, and if not, okay no problem
However, thank you so much.
Assuming your program has a central loop, or if you can simply fit keyboard checks into the processing, you are better off using Term::ReadKey than trying to fit in fork and handling the necessary inter-process communication
Calling ReadKey(-1) will do a non-blocking read, which will return a single character if a key has been hit, or undef otherwise. (You may supply a second parameter which is the IO channel to be used, but it will default to STDIN.)
I suggest you run this example. I have used sleep 1 as a dummy for your loop processing
use strict;
use warnings 'all';
use feature 'say';
use Term::ReadKey;
my $n;
while () {
my $key = ReadKey(-1);
say "key $key entered" if defined $key;
sleep 1;
say ++$n;
}
This will work for you
use 5.010;
use strict;
use Term::ReadKey;
ReadMode 4; # It will turn off controls keys (eg. Ctrl+c)
my $key;
# It will create a child so from here two processes will run.
# So for parent process fork() will return child process id
# And for child process fork() will return 0 id
my $pid = fork();
# This if block will execute by the child process and not by
# parent because for child $pid is 0
if(not $pid){
while(1){
# Do your main task here
say "hi I'm sub process and contains the main task";
sleep 2;
}
}
# Parent will skip if block and will follow the following code
while (1) {
$key = ReadKey(-1); # ReadKey(-1) will perform a non-blocked read
if($key eq 'q'){ # if key pressed is 'q'
`kill -9 $pid`; # then if will run shell command kill and kill
# the child process
ReadMode 0; # Restore original settings for tty.
exit; # Finally exit from script
} elsif( $key eq 'h' ) {
say "Hey! you pressed $key key for help";
} elsif( $key ne '' ) {
say "Hey! You pressed $key";
}
}
References:
Term::ReadKey
Fork in Perl
If you are not worried about a little delay, you can play with select and alarm to try to handle user input.
Using select you can handle the STDIN and using alarm you can wait for a few seconds or less for user input.
#!/usr/bin/env perl
use common::sense;
use IO::Select;
my $sel = IO::Select->new();
$sel->add( \*STDIN );
$|++;
my $running = 1;
while ($running) {
eval {
local $SIG{ALRM} = sub { die 'Time Out'; };
alarm 0.5;
if ( $sel->can_read(0) ) {
my $input = <STDIN>;
chomp $input;
print "STDIN > $input\n";
$running = 0;
}
};
if ( !$# ) {
select( undef, undef, undef, 0.5 );
print ".";
}
}
print "\nEnd Of Script\n";
I told you to try to use Term::ReadKey :-)
Here we go:
!/usr/bin/env perl
use common::sense;
use Term::ReadKey;
my $running = 1;
my $key;
while ($running) {
ReadMode 4; # Turn off controls keys
while ( not defined( $key = ReadKey(-1) ) ) {
select( undef, undef, undef, 0.5 );
print '.';
}
print "\nSTDIN> $key\n";
ReadMode 0;
}
print "\nEnd Of Script\n";
Now you just have to handle signals like quit or int to exit the loop, but using this you can capture the input and do whatever you want.
I was having a difficulty about perl script..I have a script that includes a loop but never breaks out of it once it gets the exit status zero.The loop will run only once the exit status results a 1 upon checking initially by the "if" statement.
my $a = "/home/vivek/generated_mdsum_reference.out";
my $b = "/home/vivek/generated_mdsum_new.out";
sub CHECK {
print "\n";
print "\n";
print "\n\tGenerating MD5SUM ....";
my $dumpfile = "/home/vivek/file_dump.dmp";
print "\n";
# my $md5sum = system("md5sum $dumpfile");
my $md5sum = `md5sum $dumpfile`;
print "\n";
print "\nChecksum: $md5sum.";
# Put checksum in file
my $ochksumfile = "/home/vivek/generated_mdsum_new.out";
open (my $fh, '>', "$ochksumfile") or die "no file:$!";
my $output = $md5sum;
die "$!" if $?;
$value = (split / /, "$output")[0];
print $fh $value;
my $status =compare($b, $a);
}
my $status =compare($b, $a);
if ( $status == 1 ){
do
CHECK;
until ($status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
All the variables I have set in there works fine, I only ended up in the until loop which it keeps on running endlessly which I think it cannot get through until the rest of the function "CHECK"
Please help me guys.
Let's look at the block in question
my $status = compare($b, $a);
if ( $status == 1 ) {
do
CHECK;
until ($status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
whether you meant it or not, this is equivalent to
my $status = compare($b, $a);
if ( $status == 1 ) {
do CHECK;
until ( $status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
So the contents of your until loop is just two print statements, which aren't going to change the value of $status so it will loop forever
What I think you meant is
my $status = compare($b, $a);
if ( $status == 1 ) {
do {
CHECK;
} until $status == 0;
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
which will repeatedly call CHECK until $status is set to zero
EXCEPT THAT you should always use lower-case characters in your local identifiers. Upper-case is reserved for global identifiers such as package names. In this situation you have unwittingly created a CHECK block
perldoc perlmod says this
Five specially named code blocks are executed at the beginning and at the end of a running Perl program. These are the BEGIN , UNITCHECK , CHECK , INIT , and END blocks.
These code blocks can be prefixed with sub to give the appearance of a subroutine (although this is not considered good style). One should note that these code blocks don't really exist as named subroutines (despite their appearance). The thing that gives this away is the fact that you can have more than one of these code blocks in a program, and they will get all executed at the appropriate moment. So you can't execute any of these code blocks by name.
So because "these code blocks don't really exist as named subroutines" your program will only call CHECK during compilation, and subsequent explicit calls will be ignored
So rename your subroutine to check and chnage your code to this
my $status = compare($b, $a);
if ( $status == 1 ) {
do {
CHECK;
} until $status == 0;
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
and all will be well
I do not understand why the until loop should stop.
In short, you get an md5 sum for a file in your "CHECK" sub.
sub CHECK {
open (my $fh, '>', "/home/vivek/generated_mdsum_new.out");
my $dump_file = "/home/vivek/file_dump.dmp";
my $md5sum = `md5sum $dump_file`;
my $value = (split(" ",$md5sum))[0];
print $fh $value;
close $fh;
compare($a,$b);
# question: what is in $a / $b and what does compare do ?
}
Now you compare $a and $b and return that result.
Assuming $a / $b contain something useful, there is nothing in the until block or CHECK block that I see to change either, so the comparison would keep returning the same result ( assuming "compare" compares and does not change anything, which would be a bad naming if it did ).
So in the until block, do something to influence the CHECK, otherwise you are stuck in a loop.
You should always use lower-case characters in your local identifiers. Upper-case is reserved for global identifiers such as package names. In this situation you have unwittingly created a CHECK block
perldoc perlmod says this
Five specially named code blocks are executed at the beginning and at the end of a running Perl program. These are the BEGIN , UNITCHECK , CHECK , INIT , and END blocks.
These code blocks can be prefixed with sub to give the appearance of a subroutine (although this is not considered good style). One should note that these code blocks don't really exist as named subroutines (despite their appearance). The thing that gives this away is the fact that you can have more than one of these code blocks in a program, and they will get all executed at the appropriate moment. So you can't execute any of these code blocks by name.
So because "these code blocks don't really exist as named subroutines" your program will only call CHECK during compilation. That means explicit calls to CHECK will be ignored, and $status never changes
Change your subroutine to check and all will be well
The problem is here:
until ($status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
Nowhere in the loop are you changing the value of $status.
The "do CHECK;" isn't part of the loop.
Would eval a file by the name of the return value of the CHECK sub
If it had actually called that sub.
Which it won't because it isn't actually a subroutine.
The other form of until is:
do {
CHECK; # doesn't work as this is a special name
} until $status == 0;
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
Which is still a problem as CHECK is a special name for a block that only gets called once at CHECK time, that you can't actually call.
Plus the code in CHECK would always have the same result, so calling it repeatedly doesn't make sense, and could still result in an infinite loop if it didn't work the first time.
This is how I might have written it
This is a first pass of making your code make sense to me, also fixing some of the errors pointed out above.
I also changed $a and $b to $ref and $new since $a and $b are reserved variables.
I improved it by using modules that come with Perl, so that I don't have to check the return values of open() and close() ( autodie ), or rely on conventions of a particular platform ( Digest::MD5, and File::Spec::Functions ).
I assume that you loaded File::Compare.
I removed the setting of $status from the check sub to reduce the use of global variables.
#! /usr/bin/env perl
use strict;
use warnings;
use 5.10.1; # set minimum version which was released in 2009
use autodie;
use File::Spec::Functions qw' catfile catdir rootdir curdir ';
use Digest::MD5;
use File::Compare qw' compare ';
# should be // not ||, but it will work if your dir isn't named "0" or ""
my $basedir = $ENV{HOME} || catdir rootdir, qw' home vivek ';
# try the current directory if it doesn't exist
$basedir = curdir unless -d $basedir;
my $ref = catfile $basedir, 'generated_mdsum_reference.out';
my $new = catfile $basedir, 'generated_mdsum_new.out';
my $dumpfile = catfile $basedir, 'file_dump.dmp';
# forward declare so that we can put them at the end
sub md5_hex_file;
sub md5_sum;
sub check;
#-------------------------------------------------------
if ( compare($new, $ref) != 0 ){
if ( check($dumpfile,$new) == 0 ){
print "\n\tfiles are now Ok. Exiting..\n";
} else {
local $| = 1; # make sure the output is flushed to STDOUT
print "\n\tfiles are NOT OK. Exiting..\n";
exit 1;
}
}
#-------------------------------------------------------
# helper subroutines
sub md5_hex_file {
my ($filename) = #_;
# let Digest::MD5 read the file for us
my $ctx = Digest::MD5->new;
{ # limit scope of $fh
open my $fh, '<', $filename;
binmode $fh;
$ctx->addfile( $fh );
close $fh;
}
$ctx->hexdigest;
}
# no longer necessary
sub md5_sum {
my ($filename) = #_;
# `md5sum -b $filename`
md5_hex_file($filename) . " $filename\n";
}
sub check {
my ( $infile, $outfile ) = #_
print "\n" x 3, "\tGenerating MD5SUM ....\n";
my $md5_hex = md5_hex_file $infile;
print "\n" x 2, "Checksum: $md5_hex.\n";
# Put checksum in file
{
open my $out_fh, '>', $outfile;
print {$out_fh} $md5_hex;
close $out_fh;
}
my $status = compare $new, $ref;
return $status if $status == 0;
# add a newline and hope that fixes it
{
open my $out_fh, '>>', $outfile;
print {$out_fh} "\n";
close $out_fh;
}
return compare $new, $ref;
}
Really I think you could have just used these "one-liners"
$ perl -Mautodie -MDigest::MD5 -e \
'open $fh, q[<], shift;
print Digest::MD5->new->addfile($fh)->hexdigest' \
file_dump.dmp > generated_mdsum_new.out
$ perl -MFile::Compare -e \
'if ( compare(shift(),shift()) == 0 ){
print qq[They match\n]
} else {
print qq[They don\'t match\n]
}' \
generated_mdsum_new.out generated_mdsum_reference.out
I have a subroutine that normally takes 1 second to run. Sometimes, it can run infinitely. I want to move on in the code if the subroutine is taking too long (> 10 seconds) and ignore that run of that subroutine. Here is what I have so far using alarm.
use Win32::OLE;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 10; # schedule alarm in 10 seconds
&do_the_subroutine;
alarm 0; # cancel the alarm
};
if ($#) {
$error_string .= $script;
#Do something else if the subroutine took too long.
}
do_the_subroutine{
# use existing instance if Excel is already running
eval {$ex = Win32::OLE->GetActiveObject('Excel.Application')};
die "Excel not installed" if $#;
unless (defined $ex) {
$ex = Win32::OLE->new('Excel.Application', sub {$_[0]->Quit;})
or die "Oops, cannot start Excel";
}
# get a new workbook
$book = $ex->Workbooks->Add;
# write to a particular cell
$sheet = $book->Worksheets(1);
$sheet->Cells(1,1)->{Value} = "foo";
# write a 2 rows by 3 columns range
$sheet->Range("A8:C9")->{Value} = [[ undef, 'Xyzzy', 'Plugh' ],
[ 42, 'Perl', 3.1415 ]];
# print "XyzzyPerl"
$array = $sheet->Range("A8:C9")->{Value};
for (#$array) {
for (#$_) {
print defined($_) ? "$_|" : "<undef>|";
}
print "\n";
}
# save and exit
$book->SaveAs( 'test.xls' );
undef $book;
undef $ex;
}
&do_the_subroutine never returns so I'm not able to move on. I'm also not able to put this block of code inside that subroutine. Any thoughts?
I suspect that what you want to do is simply not natively possible with alarm on Windows.
From perldoc perlport:
alarm Emulated using timers that must be explicitly polled whenever
Perl wants to dispatch "safe signals" and therefore cannot
interrupt blocking system calls. (Win32)
I'm running a while loop reading each line in a file, and then fork processes with the data of the line to a child. After N lines I want to wait for the child processes to end and continue with the next N lines, etc.
It looks something like this:
while ($w=<INP>) {
# ignore file header
if ($w=~m/^\D/) { next;}
# get data from line
chomp $w;
#ws = split(/\s/,$w);
$m = int($ws[0]);
$d = int($ws[1]);
$h = int($ws[2]);
# only for some days in the year
if (($m==3)and($d==15) or ($m==4)and($d==21) or ($m==7)and($d==18)) {
die "could not fork" unless defined (my $pid = fork);
unless ($pid) {
some instructions here using $m, $d, $h ...
}
push #qpid,$pid;
# when all processors are busy, wait for child processes
if ($#qpid==($procs-1)) {
for my $pid (#qpid) {
waitpid $pid, 0;
}
reset 'q';
}
}
}
close INP;
This is not working. After the first round of processes I get some PID equal to 0, the #qpid array gets mixed up, and the file starts to get read at (apparently) random places, jumping back and forth. The end result is that most lines in the file get read two or three times. Any ideas?
Thanks a lot in advance,
S.
Are you exiting inside unless ($pid)?
If not, then your child, after running the command, will add $pid of zero to the array and generally continue running what is supposed to be parent process code
I am concerned that your algorithm is not terribly efficient:
Let the base process fork processes 1 to N.
If processes 2 through N complete, before process 1, then no new processes will be started until process 1 completes.
Instead of trying to get the fiddly details of your implementation correct, use Parallel::ForkManager to get working code easily.
use strict;
use warnings;
use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new($MAX_PROCESSES);
while( my $w=<INP> ) {
next if $w=~m/^\D/; # ignore file header
chomp $w;
next unless match_dates( $w,
{ m => 3, d => 15 },
{ m => 7, d => 18 },
{ y => 2008 }, # Added this to show match_dates() capability.
);
my $pid = $pm->start and next;
.. Do stuff in child here ..
$pm->finish; # Terminate child
}
close INP;
# Returns true if ANY of the supplied date templates matches for ALL keys defined in that template.
sub match_dates {
my $string = shift;
my %target;
#target{qw( m d y )} = split(/\s/,$string);
DATE:
for my $date ( #_ ) {
my #k = keys %$match;
my $count = 0;
for( #k ) {
next DATE unless $date{$_} == $target{$_};
$count++;
}
return 1 if $count == #k; # All keys match
}
return;
}