Setting variables in perl config file eval - perl

I've got a perl script and a .config file and want to store some hashes in the config file with some variables as its value, then dynamically change them from my perl script.
Config File:
$hash{"hello"} = ["$blah", "$blah2"];
And my perl script:
if (-e ".config")
{
$blah = "hello";
$blah2 = "world!";
eval ('require(".config")');
$val1 = $hash{"hello"}[0];
$val2 = $hash{"hello"}[1];
print "$val1 $val2\n";
# Now I want to CHANGE blah and blah2
$blah = "world!";
$blah2 = "hello";
$val1 = $hash{"hello"}[0];
$val2 = $hash{"hello"}[1];
print "$val1 $val2\n";
}
But both prints show hello world! as if the change didn't happen.. Am I missing something?
Thanks.

(Strange... I've never seen a question of this sort previously, and then variations on it (which are different enough to clearly not be just a cross-post) appeared both here and on PerlMonks in the same day.)
The point you're missing is that
$hash{"hello"} = ["$blah", "$blah2"];
just copies the values of $blah and $blah2 into (an anonymous array referenced by) $hash{hello}. It does not create any lasting connection between the hash and $blah/$blah2.
As a side note, none of the quotes in that line serve any purpose. It would more commonly be written as:
$hash{hello} = [$blah, $blah2];
Or, if you want to create references so that $blah and $hash{hello}[0] are forever linked and changing one will also change the other:
$hash{hello} = [\$blah, \$blah2];
Note that in this case, you must not use quotes. Although "$blah" and $blah are equivalent, "\$blah" and \$blah are not - \$blah gives you a reference to $blah, but "\$blah" gives you the literal string "$blah" with no variables involved at all.

Related

How can I pass arguments for a subroutine from the command line

First off, my background is primarily in Python and I am relatively new at using Perl.
I am using tcsh for passing options into my .pl file.
In my code I have:
if( scalar #ARGV != 1)
{
help();
exit;
}
# Loading configuration file and parameters
our %configuration = load_configuration($ARGV[0]);
# all the scripts must be run for every configuration
my #allConfs = ("original");
sub Collimator
{
my $z = -25.0;
my %detector = init_det();
$detector{"pos"} = "0*cm 0.0*cm $z*cm"
}
foreach my $conf ( #allConfs )
{
$configuration{"variation"} = $conf ;
#other geometries ....
Collimator();
}
I want to add something that allows me to change the parameters of the subroutine in the .pl file from the command line. Currently, to generate the geometry I pass the following command into the tcsh CLI: perl file.pl config.dat. What I want is to be able to pass in something like this: perl file.pl config.dat -20.0.
I'm thinking that I need to add something to the effect of:
if($ARGV[1]){
$z = ARGV[1]}
However, I am not sure how to properly implement this. Is this something that I would specify within the subroutine itself or outside of it with the loading of the configuration file?
Use a library to handle command-line arguments, and Getopt::Long is excellent
use warnings;
use strict;
use Getopt::Long;
my ($config_file, #AllConfs);
my ($live, $dry_run) = (1, 0);
my $z;
GetOptions(
'config-file=s' => \$config_file,
'options-conf=s' => \#AllConfs,
'geom|g=f' => \$z,
'live!' => \$live,
'dry-run' => sub { $dry_run = 1; $live = 0 },
# etc
);
# Loading configuration file and parameters
# (Does it ===really=== need to be "our" variable?)
our %configuration = load_configuration($config_file);
my #data_files = #ARGV; # unnamed arguments, perhaps submitted as well
Options may be abbreviated as long as they are unambiguous so with my somewhat random sample above the program can be invoked for example as
prog.pl --conf filename -o original -o bare -g -25.0 data-file(s)
(Or whatever options for #AllConf are.) Providing explicitly g as another name for geom makes it a proper name, not an abbreviation (it doesn't "compete" with others).
Note that one can use -- or just -, and choose shorter or long(er) names for convenience or clarity etc. We get options, and there is a lot more than this little scoop, see docs.
Once the (named) options have been processed the rest on the command line is available in #ARGV, so one can mix and match arguments that way. Unnamed arguments are often used for file names. (The module offers a way to deal with those in some capacity as well.)
if ( #ARGV != 2 ) {
help();
}
my ( $path, $z ) = #ARGV;
perl gets input as list, so you can get its value using #ARGV or using $ARGV[0] and $ARGV[1]
die if ($#ARVG != 1);
#the first argument
print "$ARGV[0]\n";
#the second argument
print "$ARGV[1]\n"

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.

Reading from two files (one raw, one XMP) with ExifTool

I am new to PERL and even newer to ExifTool—and am therefore likely missing something quite basic.
The goal is to read XMP fields from a photo file. Looking at the exiftool documentation on both the ExifTool site and CPAN, I was able to read tagged jpeg and the XMP sidecar files, both without issues.
The problem is when I read from a raw file—which obviously doesn't have custom fields—I would get an error with an uninitialized value. That is to be expected.
So, I want to have code that says "if you read a field/tag from the raw file and it isn't there, look at the associated XMP file, and if that fails, return a blank string."
I therefore tried to open a second instance of ExifTool, such as:
my $exifInfo = ImageInfo($filePath);
goes to
my $exifInfoXMP = ImageInfo($filePathXMP);
But that keeps failing. If I read the XMP directly from the get-go, it works just fine, so I am getting the impression that I cannot read two ExifTool structures at the same time (which can't be right; I have to be the error here). The code below works, but I cannot "interleave" the conditionals on the two files. I have to process the raw first, then run a second pass with a new handler for the XMP. Knowing how efficient PERL is, my approach cannot possibly be a good one (even though it does the job).
In particular, there is one line that puzzles me. If I remove it, nothing works. (it should be well marked).
$filePath =~ s/$photoExtensions$/.XMP/i;
That line essential does the same as reading the XMP from the get-go (not my ideal solution).
Anyone have an idea as to where I am messing up?
Thanks,
Paul
header [EDITED TO SHOW ALL OPTIONS; HAD SHOWN ALL USED IN QUESTION]
#!/usr/bin/perl
# load standard packages
use strict;
use warnings;
use Data::Dumper;
use File::Find;
no warnings 'File::Find';
use Image::ExifTool ':Public';
# define proxy for ExifTool
my $exifTool = new Image::ExifTool;
my $exifToolXMP = new Image::ExifTool;
# turn on immediate updates
$|=1;
# common extensions that I want to recognize
my $photoExtensions = "\.(jpg|crw|cr2|cr3|rw2|orf|raw|nef|arw|dng)";
my $imageExtensions = "\.(tiff|tif|psd|png|eps|hdr|exr|svg|gif|afphoto|pdf)";
my $videoExtensions = "\.(flv|vob|ogv|avi|mts|m2ts|mov|qt|wmv|mp4|m4p|m4v|svi|3gp|3g2)";
my $audioExtensions = "\.(aiff|aac|wav|mp3|m4a|m4p|ogg|wma)";
my $appFileExtensions = "\.(on1|cos|cof)";
my $GPSFileExtensions = "\.(gpx|kml|kmz|log)";
# start main program
main();
routine in question
sub listKeywords {
print "Reads and displays file information from certain tags (typically set in Photomechanic):\n";
print "\t1. Subject\n";
print "\t2. Hierarchical Subject\n";
print "\t3. Supplemental Categories\n";
print "\t4. Label Name 1\n";
print "\t5. Label Name 2\n";
print "\t6. Label Name 3\n";
print "\t7. Label Name 4\n\n";
print "List Keywords ---\n\tEnter file name (with path) --> ";
my $filePath = <STDIN>;
chomp $filePath;
$filePath =~ s/\\//g;
$filePath =~ s/\s+$//;
########################################################
# COMMENT OUT THE FOLLOWING LINE AND NOTHING WORKS;
# $filePathXMP should be defined anyway, which suggests to
# me that the second invocation of ImageInfo doesn't actually occur.
# But I don't understand why.
$filePath =~ s/$photoExtensions$/.XMP/i;
print "\n\n";
my $filePathXMP = $filePath;
$filePathXMP =~ s/$photoExtensions$/.XMP/i; # TO FIX: filename may not have uppercase extension
# Get Exif information from image file
my $exifInfo = $exifTool->ImageInfo($filePath);
# my $exifInfoXMP = $exifToolXMP->ImageInfo($filePath =~ s/$photoExtensions$/.XMP/gi);
print "XMP Sidecar: \[$filePathXMP\]\n\n";
########################################################
# Get Specific Tag Value
my $hierarchicalSubject = $exifTool->GetValue('HierarchicalSubject');
my $subject = $exifTool->GetValue('Subject');
my $supplementalCategories = $exifTool->GetValue('SupplementalCategories');
my $labelName1 = $exifTool->GetValue('LabelName1');
my $labelName2 = $exifTool->GetValue('LabelName2');
my $labelName3 = $exifTool->GetValue('LabelName3');
my $labelName4 = $exifTool->GetValue('LabelName4');
my $exifInfo = ImageInfo($filePathXMP);
if (not defined $hierarchicalSubject) {$hierarchicalSubject = $exifTool->GetValue('HierarchicalSubject');}
if (not defined $hierarchicalSubject) {$hierarchicalSubject = "";}
if (not defined $subject) {$subject = $exifTool->GetValue('Subject');}
if (not defined $subject) {$subject = "";}
if (not defined $supplementalCategories) {$supplementalCategories = $exifTool->GetValue('SupplementalCategories');}
if (not defined $supplementalCategories) {$supplementalCategories = "";}
if (not defined $labelName1) {$labelName1 = $exifTool->GetValue('LabelName1');}
if (not defined $labelName1) {$labelName1 = "";}
if (not defined $labelName2) {$labelName2 = $exifTool->GetValue('LabelName2');}
if (not defined $labelName2) {$labelName2 = "";}
if (not defined $labelName3) {$labelName3 = $exifTool->GetValue('LabelName3');}
if (not defined $labelName3) {$labelName3 = "";}
if (not defined $labelName4) {$labelName4 = $exifTool->GetValue('LabelName4');}
if (not defined $labelName4) {$labelName4 = "";}
print "Subject:\n------------------------------\n$subject\n\n";
print "Hierarchical Subject:\n------------------------------\n$hierarchicalSubject\n\n";
print "Supplemental Categories:\n------------------------------\n$supplementalCategories\n\n";
print "Label Name 1:\n------------------------------\n$labelName1\n\n";
print "Label Name 2:\n------------------------------\n$labelName2\n\n";
print "Label Name 3:\n------------------------------\n$labelName3\n\n";
print "Label Name 4:\n------------------------------\n$labelName4\n\n";
}
As your code is incomplete, I have to ask: did you make sure to start your script with the following lines?
use strict;
use warnings;
Those two lines are not there to annoy you, they will protect you from simple mistakes you might have made in your code.
IMHO the real problem with your sub listKeywords() is the following line:
my $exifInfo = ImageInfo($filePathXMP);
There are two problems here:
you redefine the variable $exifInfo from a few lines before.
you are not using the OO approach for the 2nd image info.
I think what you intended to write was the following line:
my $exifInfoXMP = $exifToolXMP->ImageInfo($filePathXMP);

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

Recommended method to refactor variable name in Perl code?

I can use Perltidy to reformat source. Quite useful.
If a source file uses a variable like #m, how can I most easily refactor that into something else, e.g. #miles_travelled?
Using a regular expression to rename does not appear safe, because a separate variable such as $m may also exist (with a different type, in this case a scalar), yet the #m variable can be referenced using an expression like $m[$i].
For example, none of the following will be correct for Perl code:
s/([\$\#])m/$1miles_travelled/g # Will rename scalar with same name
s/\$m/\$miles_travelled/g # Will fail to rename accesses of array
Is there a recommended tool or method for safely renaming a variable name in Perl code?
The variable $m always occurs as $m.
The variable #m always occurs as #m or $m[...].
The variable %m always occurs as %m or $m{...} or #m{...}.
… except with indirect method calls: new $m[...] parses as $m->new([...]). But we can probably ignore this case (use no indirect to make sure).
If we want to cover the first three cases properly, we can
replace a scalar by s/(?<=\$)OLDNAME(?!\s*[\[\{])/NEWNAME/g
replace an array by s/(?<=\#)OLDNAME(?!\{)|(?<=\$)OLDNAME(?=\s*\[)/NEWNAME/g
replace a hash by s/(?<=\%)OLDNAME|(?<=[\$\#])OLDNAME(?=\s*\{)/NEWNAME/g
Note that lookarounds or multiple passes for the different cases are neccessary.
Test:
use Test::More tests => 3;
my $scalar_re = qr/(?<=\$) foo (?!\s*[\[\{])/x;
my $array_re = qr/(?<=\#) foo (?!\{) | (?<=\$) foo (?=\s*\[)/x;
my $hash_re = qr/(?<=\%) foo | (?<=[\$\#]) foo (?=\s*\{)/x;
my $input = '$foo, $foo[1], #foo, $foo{a}, %foo, #foo{qw/a b/}';
my $scalar = '$bar, $foo[1], #foo, $foo{a}, %foo, #foo{qw/a b/}';
my $array = '$foo, $bar[1], #bar, $foo{a}, %foo, #foo{qw/a b/}';
my $hash = '$foo, $foo[1], #foo, $bar{a}, %bar, #bar{qw/a b/}';
is $input =~ s/$scalar_re/bar/xrg, $scalar;
is $input =~ s/$array_re /bar/xrg, $array;
is $input =~ s/$hash_re /bar/xrg, $hash;
The Padre editor will carry out a small number of simple refactorings automatically for you. "Rename variable" is one of them.