in Perl, how to assign the print function to a variable? - perl

I need to control the print method using a variable
My code is below
#!/usr/bin/perl
# test_assign_func.pl
use strict;
use warnings;
sub echo {
my ($string) = #_;
print "from echo: $string\n\n";
}
my $myprint = \&echo;
$myprint->("hello");
$myprint = \&print;
$myprint->("world");
when I ran, I got the following error for the assignment of print function
$ test_assign_func.pl
from echo: hello
Undefined subroutine &main::print called at test_assign_func.pl line 17.
Looks like I need to prefix a namespace to print function but I cannot find the name space. Thank you for any advice!

print is an operator, not a sub.
perlfunc:
The functions in this section can serve as terms in an expression. They fall into two major categories: list operators and named unary operators.
Perl provides a sub for named operators that can be duplicated by a sub with a prototype. A reference to these can be obtained using \&CORE::name.
my $f = \&CORE::length;
say $f->("abc"); # 3
But print isn't such an operator (because of the way it accepts a file handle). For these, you'll need to create a sub with a more limited calling convention.
my $f = sub { print #_ };
$f->("abc\n");
Related:
What are Perl built-in operators/functions?

As mentioned in CORE, some functions can't be called as subroutines, only as barewords. print is one of them.

Related

Perl: Variable value is 'glob', but should be 'scalar'

I do have the following simple code:
my $TimeZone = $hCache->{'TimeZone'}; # Cache gets filled earlier
my $DateTime = DateTime->now();
$DateTime->set_time_zone($TimeZone);
This code runs in an application server which is basically a long running perl process that accepts incoming network connections.
From time to time this applicationserver gets somehow "dirty", and the code above is printing the following error:
The 'name' parameter ("Europe/Berlin") to DateTime::TimeZone::new was
a 'glob', which is not one of the allowed types: scalar at
/srv/epages/eproot/Perl/lib/site_perl/linux/DateTime.pm line 1960.
When I try to debug the variable "$TimeZone" I'm getting no further details.
E.g.
print ref($TimeZone); # prints nothing (scalar?)
print $TimeZone; # prints "Europe/Berlin"
The code works if I'm forcing the timezone to be a string again, like so:
my $TimeZone = $hCache->{'TimeZone'}; # Cache gets filled earlier
my $DateTime = DateTime->now();
$DateTime->set_time_zone($TimeZone."");
My questions are:
If 'glob' is not a reference, how can I debug the variable properly?
How can I create a 'glob' variable? What is the syntax to it? I'm
quite sure that my huge codebase has some accidents in it, but I
don't know what to search for.
Is there a way to 'monitor' the
variable? Basically, getting a stacktrace if the variable changes
How can I create a 'glob' variable?
Glob, short for "typeglob" is a structure (in the C sense of the word) that contains a field for each type of variable that can be found in the symbol table (scalar, array, hash, code, glob, etc). They form the symbol table.
Globs are created by simply mentioning a package variable.
#a = 4..6; # Creates glob *main::a containing a reference to the new array.
Since globs are themselves packages variables, you can bring a glob into existence just by mentioning it.
my $x = *glob; # The glob *main::glob is created by this line at compile-time.
Note that file handles are often accessed via globs. For example, open(my $fh, '<', ...) populates $fh with a reference to a glob that contains a reference to an IO.
$fh # Reference to glob that contains a reference to an IO.
*$fh # Glob that contains a reference to an IO.
*$fh{IO} # Reference to an IO.
If 'glob' is not a reference, how can I debug the variable properly?
ref(\$var) will return GLOB for a glob.
$ perl -e'$x = *STDOUT; CORE::say ref(\$x)'
GLOB
Is there a way to 'monitor' the variable?
Yes. You can add magic to it.
$ perl -e'
use feature qw( say );
use Carp qw( cluck );
use Variable::Magic qw( wizard cast );
my $wiz = wizard(
data => sub { $_[1] },
set => sub { cluck("Variable $_[1] modified"); },
);
my $x;
cast($x, $wiz, q{$x});
$x = 123; # Line 14
'
Variable $x modified at -e line 9.
main::__ANON__(SCALAR(0x50bcee23c0), "\$x") called at -e line 14
eval {...} called at -e line 14
More work is needed to detect if a hash or array changes, but the above can be used to monitor the elements of hashes and arrays.

Assign number to variable in perl subroutine [assign returned list to variables]

I am trying to call a multiple variable from a subroutine but when i try to print, it just print only one variable.
In python the script will be f=mode()[0] and b=mode()[1] and it works.
subroutinefile a.pl
sub mode() {
my ($f, $b);
$f=41;
$b=2;
return ($f,$b);
}
And another file that calls the a.pl
use strict;
use warnings;
require 'a.pl';
my ($f,$b);
$f= mode(0);
$b= mode(1);
print "$f\n";
print "$b\n";
The problem is it only prints 2 for both f and b.
You have passed arguments to subroutine: $f = mode(0); $b = mode(1);.
Try the following:
my ($f,$b) = mode();
Your subroutine returns a fixed length list. You may assign values in the list to your variables.
The problem is the mismatch between the definition of the subroutine (no parameters) and the way you are trying to call it (one argument).
Change those calls to this and it works:
$f= (mode())[0];
$b= (mode())[1];
Think there is a syntactical difference between getting an array index FROM the return set versus sending an argument; function(xxx) sends the argument, (function()[])is using an array slice of the return.

Issues with function calls for search routine

My aim is to have multiple searches of specific files recursively. So I have these files:
/dir/here/tmp1/recursive/foo2013.log
/dir/here/tmp1/recursive/foo2014.log
/dir/here/tmp2/recursive/foo2013.log
/dir/here/tmp2/recursive/foo2014.log
where the 2013 and 2014 says in which year the files got modified lastly.
I then want to find the more up to date files (foo2014.log) for each directory tree (tmp1 and tmp2 likewise).
Referring to this answer I have the following code in script.pl:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
func("tmp1");
print "===\n";
func("tmp2");
sub func{
my $varName = shift;
my %times;
find(\&upToDateFiles, "/dir/here");
for my $dir (keys %times) {
if ($times{$dir}{file} =~ m{$varName}){
print $times{$dir}{file}, "\n";
# do stuff here
}
}
sub upToDateFiles {
return unless (-f && /^foo/);
my $mod = -M $_;
if (!defined($times{$File::Find::dir})
or $mod < $times{$File::Find::dir}{mod})
{
$times{$File::Find::dir}{mod} = $mod;
$times{$File::Find::dir}{file} = $File::Find::name;
}
}
}
which will give me this output:
Variable "%times" will not stay shared at ./script.pl line 25.
/dir/here/tmp1/recursive/foo2014.log
===
I have three questions:
Why isn't the second call of the function func working like the first one? Variables are just defined in the scope of the function so why am I getting interferences?
Why do I get the notification for variable %times and how can I get rid of it?
If I define the function upToDateFiles outside of func I am getting this error: Execution of ./script.pl aborted due to compilation errors. I think this is because the variables aren't defined outside of func. Is it possible to change this and still get the desired output?
For starters - embedding a sub within another sub is rather nasty. If you use diagnostics; you'll get:
(W closure) An inner (nested) named subroutine is referencing a
lexical variable defined in an outer named subroutine.
When the inner subroutine is called, it will see the value of
the outer subroutine's variable as it was before and during the *first*
call to the outer subroutine; in this case, after the first call to the
outer subroutine is complete, the inner and outer subroutines will no
longer share a common value for the variable. In other words, the
variable will no longer be shared.
This problem can usually be solved by making the inner subroutine
anonymous, using the sub {} syntax. When inner anonymous subs that
reference variables in outer subroutines are created, they
are automatically rebound to the current values of such variables.
Which is directly relevant to the problem you're having. Try to avoid nesting your subs, and you won't have this problem. It certainly looks like you're trying to be far more complicated than you need to. Have you considered something like:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use File::Find;
my %filenames;
sub compare_tree {
return unless -f && m/^foo/;
my $mtime = -M $File::Find::name;
if ( !$filenames{$_} || $mtime < $filenames{$_}{mtime} ) {
$filenames{$_} = {
newest => $File::Find::name,
mtime => $mtime,
};
}
}
find( \&compare_tree, "/dir/here" );
foreach my $filename ( keys %filenames ) {
print "$filename has newest version path of:", $filenames{$filename}{newest}, "\n";
print "$filename has newest mtime of:", $filenames{$filename}{mtime}, "\n";
}
I'd also note - you seem to be using $File::Find::dir - this looks wrong to me, based on what you describe you're doing. Likewise - you're running find twice on the same directory structure, which is not a very efficient approach - very big finds are expensive operations, so doubling the work needed isn't good.
Edit: Caught out by forgetting that -M was: -M Script start time minus file modification time, in days.. So 'newer' files are the lower number, not the higher. (So have amended above accordingly).

2 Sub references as arguments in perl

I have perl function I dont what does it do?
my what does min in perl?
#ARVG what does mean?
sub getArgs
{
my $argCnt=0;
my %argH;
for my $arg (#ARGV)
{
if ($arg =~ /^-/) # insert this entry and the next in the hash table
{
$argH{$ARGV[$argCnt]} = $ARGV[$argCnt+1];
}
$argCnt++;
}
return %argH;}
Code like that makes David sad...
Here's a reformatted version of the code doing the indentations correctly. That makes it so much easier to read. I can easily tell where my if and loops start and end:
sub getArgs {
my $argCnt = 0;
my %argH;
for my $arg ( #ARGV ) {
if ( $arg =~ /^-/ ) { # insert this entry and the next in the hash table
$argH{ $ARGV[$argCnt] } = $ARGV[$argCnt+1];
}
$argCnt++;
}
return %argH;
}
The #ARGV is what is passed to the program. It is an array of all the arguments passed. For example, I have a program foo.pl, and I call it like this:
foo.pl one two three four five
In this case, $ARGV is set to the list of values ("one", "two", "three", "four", "five"). The name comes from a similar variable found in the C programming language.
The author is attempting to parse these arguments. For example:
foo.pl -this that -the other
would result in:
$arg{"-this"} = "that";
$arg{"-the"} = "other";
I don't see min. Do you mean my?
This is a wee bit of a complex discussion which would normally involve package variables vs. lexically scoped variables, and how Perl stores variables. To make things easier, I'm going to give you a sort-of incorrect, but technically wrong answer: If you use the (strict) pragma, and you should, you have to declare your variables with my before they can be used. For example, here's a simple two line program that's wrong. Can you see the error?
$name = "Bob";
print "Hello $Name, how are you?\n";
Note that when I set $name to "Bob", $name is with a lowercase n. But, I used $Name (upper case N) in my print statement. As it stands, now. Perl will print out "Hello, how are you?" without a care that I've used the wrong variable name. If it's hard to spot an error like this in a two line program, imagine what it would be like in a 1000 line program.
By using strict and forcing me to declare variables with my, Perl can catch that error:
use strict;
use warnings; # Another Pragma that should always be used
my $name = "Bob";
print "Hello $Name, how are you doing\n";
Now, when I run the program, I get the following error:
Global symbol "$Name" requires explicit package name at (line # of print statement)
This means that $Name isn't defined, and Perl points to where that error is.
When you define variables like this, they are in scope with in the block where it's defined. A block could be the code contained in a set of curly braces or a while, if, or for statement. If you define a variable with my outside of these, it's defined to the end of the file.
Thus, by using my, the variables are only defined inside this subroutine. And, the $arg variable is only defined in the for loop.
One more thing:
The person who wrote this should have used the Getopt::Long module. There's a major bug in their code:
For example:
foo.pl -this that -one -two
In this case, my hash looks like this:
$args{'-this'} = "that";
$args{'-one'} = "-two";
$args{'-two'} = undef;
If I did this:
if ( defined $args{'-two'} ) {
...
}
I would not execute the if statement.
Also:
foo.pl -this=that -one -two
would also fail.
#ARGV is a special variable (refer to perldoc perlvar):
#ARGV
The array #ARGV contains the command-line arguments intended for the
script. $#ARGV is generally the number of arguments minus one, because
$ARGV[0] is the first argument, not the program's command name itself.
See $0 for the command name.
Perl documentation is also available from your command line:
perldoc -v #ARGV

Perl argument parsing into functions when no parentheses are used

I have the following test code:
sub one_argument {
($a) = #_;
print "In one argument: \$a = $a\n";
return "one_argument";
}
sub mul_arguments {
(#a) = #_;
return "mul_argument";
}
print &one_argument &mul_arguments "something", "\n";
My goal is to be able to understand a bit better how perl decides which arguments to go into each function, and to possibly clear up any misunderstandings that I might have. I would've expected the above code to output:
In one argument: mul_argument
one_argument
However, the below is output:
Use of uninitialized value $a in concatenation (.) or string at ./test.pl line 5.
In one argument: $a =
mdd_argument
I don't understand where 'mdd_argument' comes from (Is it a sort of reference to a function?), and why one_argument receives no arguments.
I would appreciate any insight as to how perl parses arguments into functions when they are called in a similar fashion to above.
Please note that this is purely a learning exercise, I don't need the above code to perform as I expected, and in my own code I wouldn't call a function in such a way.
perldoc perlsub:
If a subroutine is called using the & form, the argument list is optional, and if omitted, no #_ array is set up for the subroutine: the #_ array at the time of the call is visible to subroutine instead. This is an efficiency mechanism that new users may wish to avoid.
In other words, in normal usage, if you use the &, you must use parentheses. Otherwise, the subroutine will be passed the caller's #_.
The mysterious "mdd" is caused because &one_argument doesn't have any arguments and perl is expecting an operator to follow it, not an expression. So the & of &mul_arguments is actually interpreted as the stringwise bit and operator:
$ perl -MO=Deparse,-p -e 'sub mul_arguments; print &one_argument &mul_arguments "something", "\n"'
print((&one_argument & mul_arguments('something', "\n")));
and "one_argument" & "mul_arguments" produces "mdd_argument".