I am trying to store my log messages in a hash depending upon message type as shown below:
#!/usr/bin/perl
use strict;
use warnings;
my %log;
opendir (DIR, '.') or die $!;
while (my $file = readdir(DIR)) {
# some code to select TAR filename in $1
if (-e $1.'.tar') {
push(#{$log->{$1}}, $file); /* line 12 */
}
else {
$log{$1} = [];
push(#{$log->{$1}}, $file); /* line 16 */
}
Now this code gives compilation error saying:
Global symbol "$log" requires explicit package name at at lines 12 & 16
where I am actually trying to use the hash "%log". What can be a possible way to get rid of this error ? Why exactly is this happening ?
I did see some explanation on context where people replied saying the variables were created in one context and were being referred in another but I feel this variable should be available inside while loop in this piece of code. This happens only when I have "use strict" and works fine otherwise.
I have started with Perl so I do not fully understand the basics! Please help me understand why this variable is not accessible.
my %log;
defines hash %log, but lines 12 and 16 don't use it. Instead, you're accessing the anonymous hash referenced by the scalar $log which you've never declared. You have two options.
You could continue to use an anonymous hash.
my $log = {}; # The creation of the hash ("{}") is currently being done
# implicitly by "->". This is called autovivification.
... $log->{...} ...
This adds a bit of a extra complexity and an unnoticeable reduction in speed.
You could use use a hash directly.
my %log;
... $log{...} ...
I'm not sure what are you trying to do with $1, but the hash access is not a reference, so change:
$log->{$1}
to
$log{$1}
The error message you got, says: Global symbol "$log" requires explicit package, because $log variable was not defined. Remember that %log and $log are two different variables (hash vs scalar).
Related
I'm new to gdbm and I would like to use it in Perl. I know that Perl ships by default with a module for that (GDBM_File). Now, when I try the simplest example possible, namely:
#!/usr/bin/perl
use strict;
use warnings;
use GDBM_File;
my $dbfile = '/tmp/test.gdbm';
my $ok = tie(my %db, 'GDBM_File', $dbfile, &GDBM_WRCREAT, 0664);
die "can't tie to $dbfile for WRCREAT access: $!" unless $ok;
$db{test} = 1;
untie %db;
and execute it I get the following warning:
untie attempted while 1 inner references still exist at ./gdbm-test line 13.
I read the perl documentation (see the "untie gotcha" in the provided link) but that explanation does not seem to apply here since it is clear that %db has no references anywhere in the code pointing to it.
Nonetheless the code seems to work since when I inspect the database file I get the correct result:
bash$ echo list | gdbmtool /tmp/test.gdbm
test 1
Why does this warning appear and how can I get rid of it?
I think that this is, in fact, a manifestation of the gotcha that you point to. The documentation for tie() says this:
The object returned by the constructor is also returned by the tie function
So your $ok contains a reference to the object, and you should undefine that before calling untie().
undef $ok;
untie %db;
I am working on a program which uses different subroutines in separate files.
There are three parts
A text file with the name of the subroutine
A Perl program with the subroutine
The main program which extracts the name of the subroutine and launches it
The subroutine takes its data from a text file.
I need the user to choose the text file, the program then extracts the name of the subroutine.
The text file contains
cycle.name=cycle01
Here is the main program :
# !/usr/bin/perl -w
use strict;
use warnings;
use cycle01;
my $nb_cycle = 10;
# The user chooses a text file
print STDERR "\nfilename: ";
chomp($filename = <STDIN>);
# Extract the name of the cycle
open (my $fh, "<", "$filename.txt") or die "cannot open $filename";
while ( <$fh> ) {
if ( /cycle\.name/ ) {
(undef, $cycleToUse) = split /\s*=\s*/;
}
}
# I then try to launch the subroutine by passing variables.
# This fails because the subroutine is defined as a variable.
$cycleToUse($filename, $nb_cycle);
And here is the subroutine in another file
# !/usr/bin/perl
package cycle01;
use strict;
use warnings;
sub cycle01 {
# Get the total number of arguments passed
my ($filename, $nb_cycle) = #_;
print "$filename, $nb_cycle";
Your code doesn't compile, because in the final call, you have mistyped the name of $nb_cycle. It's helpful if you post code that actually runs :-)
Traditionally, Perl module names start with a capital letter, so you might want to rename your package to Cycle01.
The quick and dirty way to do this is to use the string version of eval. But evaluating an arbitrary string containing code is dangerous, so I'm not going to show you that. The best way is to use a dispatch table - basically a hash where the keys are valid subroutine names and the values are references to the subroutines themselves. The best place to add this is in the Cycle01.pm file:
our %subs = (
cycle01 => \&cycle01,
);
Then, the end of your program becomes:
if (exists $Cycle01::subs{$cycleToUse}) {
$Cycle01::subs{$cycleToUse}->($filename, $nb_cycle);
} else {
die "$cycleToUse is not a valid subroutine name";
}
(Note that you'll also need to chomp() the lines as you read them in your while loop.)
To build on Dave Cross' answer, I usually avoid the hash table, partly because, in perl, everything is a hash table anyway. Instead, I have all my entry-point subs start with a particular prefix, that prefix depends on what I'm doing, but here we'll just use ep_ for entry-point. And then I do something like this:
my $subname = 'ep_' . $cycleToUse;
if (my $func = Cycle01->can($subname))
{
$func->($filename, $nb_cycle);
}
else
{
die "$cycleToUse is not a valid subroutine name";
}
The can method in UNIVERSAL extracts the CODE reference for me from perl's hash tables, instead of me maintaining my own (and forgetting to update it). The prefix allows me to have other functions and methods in that same namespace that cannot be called by the user code directly, allowing me to still refactor code into common functions, etc.
If you want to have other namespaces as well, I would suggest having them all be in a single parent namespace, and potentially all prefixed the same way, and, ideally, don't allow :: or ' (single quote) in those names, so that you minimise the scope of what the user might call to only that which you're willing to test.
e.g.,
die "Invalid namespace $cycleNameSpaceToUse"
if $cycleNameSpaceToUse =~ /::|'/;
my $ns = 'UserCallable::' . $cycleNameSpaceToUse;
my $subname = 'ep_' . $cycleToUse;
if (my $func = $ns->can($subname))
# ... as before
There are definitely advantages to doing it the other way, such as being explicit about what you want to expose. The advantage here is in not having to maintain a separate list. I'm always horrible at doing that.
Not entirely sure why but for some reason i cant print the hash value outside the while loop.
#!/usr/bin/perl -w
opendir(D, "cwd" );
my #files = readdir(D);
closedir(D);
foreach $file (#files)
{
open F, $file or die "$0: Can't open $file : $!\n";
while ($line = <F>) {
chomp($line);
$line=~ s/[-':!?,;".()]//g;
$line=~ s/^[a-z]/\U/g;
#words = split(/\s/, $line);
foreach $word (#words) {
$frequency{$word}++;
$counter++;
}
}
close(F);
print "$file\n";
print "$ARGV[0]\n";
print "$frequency{$ARGV[0]}\n";
print "$counter\n";
}
Any help would be much appreciated!
cheers.
This line
print "$frequency{$ARGV[0]}\n";
Expects you to have an argument to your script, e.g. perl script.pl argument. If you have no argument, $ARGV[0] is undefined, but it will stringify to the empty string. This empty string is a valid key in the hash, but the value is undefined, hence your warning
Use of uninitialized value within %frequency in concatenation (.) or string
But you should also see the warning
Use of uninitialized value $ARGV[0] in hash element
And it is a very big mistake not to include that error in this question.
Also, when using readdir, you get all the files in the directory, including directories. You might consider filtering the files somewhat.
Using
use strict;
use warnings;
Is something that will benefit you very much, so add that to your script.
I had originally written this,
There is no %frequency defined at the top level of your program.
When perl sees you reference %frequency inside the inner-most
loop, it will auto-vivify it, in that scratchpad (lexical scope).
This means that when you exit the inner-most loop (foreach $word
(#words)), the auto-vivified %frequency is out of scope and
garbage-collected. Each time you enter that loop, a new, different
variable will be auto-vivified, and then discarded.
When you later refer to %frequency in your print, yet another new,
different %frequency will be created.
… but then realized that you had forgotten to use strict, and Perl was being generous and giving you a global %frequency, which ironically is probably what you meant. So, this answer is wrong in your case … but declaring the scope of %frequency would probably be good form, regardless.
These other, “unrelated” notes are still useful perhaps, or else I'd delete the answer altogether:
As #TLP mentioned, you should probably also skip directories (at least) in your file loop. A quick way to do this would be my #files = grep { -f "cwd/$_" } (readdir D); this will filter the list to contain only files.
I'm further suspicious that you named a directory "cwd" … are you perhaps meaning the current working directory? In all the major OS'es in use today, that directory is referenced as “.” — you're looking for a directory literally named "cwd"?
I've just been caught out when using someone else's API in conjunction with the default variable $_
foreach (#rps_server_details) {
#server_data = ();
#server_data = split(/,/);
#$esp_hosts = ();
$filters{server_name} = $server_data[0];
print "--->$_<--\n";
$esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$#";
print "--->$_<--\n";
The output for this is:
--->igrid8873.someone.com,app_10<--
Use of uninitialized value in concatenation (.) or string at ./rps_inv_lookup.pl line 120.
---><--
Specifying my own loop variable instead of relying on $_ fixes the problem.
Am I just being naive by using $_ in conjunction with an API someone else has written? Or is this a bug in that API module?
It is a bug in the API. If you use $_ in a function it is important to add a
local($_);
inside the function to avoid clobbering the caller's $_, or otherwise avoid using $_in a library function to be called by others.
If you can limit yoursel to Perl versions > 5.9.1 then you can also make $_ lexical which makes it easier to understand than localwith
my $_;
But this will break on earlier versions of Perl.
From man perlvar:
As $_ is a global variable, this may lead in some cases to
unwanted side-effects. As of perl 5.9.1, you can now use a
lexical version of $_ by declaring it in a file or in a block
with "my". Moreover, declaring "our $_" restores the global $_
in the current scope.
I would say it's:
a violation of best practices on your part (always use as-local-as possible variable scope and avoid using $_ due to just the issue your encountered)
coupled with a bug in the API caused by the same violation of the best practices as well as not localizing the special variable with local $_ as proscribed by perldoc perlvar.
In addition to perldoc, the API violates Perl Best Practices (as in Conway's book's rules):
Section 5.6. Localizing Punctuation Variables
If you're forced to modify a punctuation variable, localize it.
The problems described earlier under "Localization can also crop up whenever you're forced to change the value in a punctuation variable (often in I/O operations). All punctuation variables are global in scope. They provide explicit control over what would be completely implicit behaviours in most other languages: output buffering, input line numbering, input and output line endings, array indexing, et cetera.
It's usually a grave error to change a punctuation variable without first localizing it. Unlocalized assignments can potentially change the behaviour of code in entirely unrelated parts of your system, even in modules you did not write yourself but are merely using.
Using local is the cleanest and most robust way to temporarily change the value of a global variable. It should always be applied in the smallest possible scope, so as to minimize the effects of any "ambient behaviour" the variable might control:
Here's full perldoc perlvar documentation as well - search for the word "nasty_break" in the web page (I couldn't find direct in-page link but it's close to the start of the page)
You should be very careful when
modifying the default values of most
special variables described in this
document. In most cases you want to
localize these variables before
changing them, since if you don't, the
change may affect other modules which
rely on the default values of the
special variables that you have
changed. This is one of the correct
ways to read the whole file at once:
open my $fh, "<", "foo" or die $!;
local $/; # enable localized slurp mode
my $content = ;
close $fh;
But the following code is quite bad:
open my $fh, "<", "foo" or die $!;
undef $/; # enable slurp mode
my $content = ;
close $fh;
since some other module, may want to
read data from some file in the
default "line mode", so if the code we
have just presented has been executed,
the global value of $/ is now changed
for any other code running inside the
same Perl interpreter.
Usually when a variable is localized
you want to make sure that this change
affects the shortest scope possible.
So unless you are already inside some
short {} block, you should create one
yourself. For example:
my $content = '';
open my $fh, "<", "foo" or die $!;
{
local $/;
$content = ;
}
close $fh;
Here is an example of how your own
code can go broken:
for (1..5){
nasty_break();
print "$_ ";
}
sub nasty_break {
$_ = 5;
# do something with $_
}
You probably expect this code to
print:
1 2 3 4 5
but instead you get:
5 5 5 5 5
Why? Because nasty_break() modifies $_
without localizing it first. The fix
is to add local():
local $_ = 5;
foreach (#rps_server_details) {
#server_data = ();
#server_data = split(/,/);
#$esp_hosts = ();
$filters{server_name} = $server_data[0];
print "--->$_<--\n";
{
local *_; # disconnects the remaining scope from the implicit
# variables so you can clean up after the dirty api.
# NOTE: Submit a bug report against the offending module.
# If you notice this across multiple api features
# consider finding a different module for this task.
$esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$#";
}
print "--->$_<--\n";
I am using use strict; in perl and with that I use the following statement.
unless(defined($x)){
print "Not defined";
}
Where $x is not declared anywhere. So I expect it to print "Not defined" but it returns an error
Global symbol "$x" requires explicit package name at *********** in line 15.
The strict pragma has three parts: strict references, strict variables, and strict subs. The one you're running into is
strict vars
This generates a compile-time error if you access a variable that wasn't declared via our or use vars, localized via my, or wasn't fully qualified. Because this is to avoid variable suicide problems and subtle dynamic scoping issues, a merely local variable isn't good enough.
Because it generates compile-time errors, your non-BEGIN code won't even have a chance to run. You can temporarily allow non-strict variables inside a block as in
{
no strict 'vars';
print "Not defined!\n" unless defined $x;
}
but note that Perl's defined operator tells you whether a value is defined, not whether a variable has been declared.
Tell us more about your application, and we can give you better advice about how to handle it.
You can't even refer to a variable unless it's declared. When you ask
defined( $x ) ?
the compiler is going to complain: I don't know this what you're asking about, how am I supposed to tell it is defined? It has no point of reference for that variable, since you've indicated you do not want variables auto-created by name.
If strict 'vars' was not on--which it is by default when you use strict--then it would create an entry in the package symbol table for 'x'.
Interestingly enough is that without strict 'refs' it is also easy to check if a variable is in the package symbol table.
defined( *{ __PACKAGE__ . '::x' }{SCALAR} )
Since there is no way to auto-create a lexical ("my variables"), there is also not a standard way to check to see if lexicals are declared. Lexical variables are stored in the "pad". But there is a module PadWalker that can help.
In order to check the current level, you could get a hash of the pad, and then check whether or not it exists in the current pad. You could also loop back up through the stack (the integer argument works something like caller) to find where the most recent x was.
my $h = peek_my (0);
exists $h->{x};
I think you are mixing 'defined' and 'declared' concepts.
Your are asking for 'How to check if variable is declared in perl' but then you are checking if a variable is defined. These are two different concepts.
In perl if you use 'use strict' you are automatically checking for any variable not declared (using my, local or our). Once you have a variable declared, you can test if it is defined (have a value assigned).
So in your test, you are missing a prior declaration before testing for defineness
use strict;
my $x; # you are missing this part
[...] | # code
# your test for define
print defined $x? "defined\n" : "not defined\n";
Please be aware the testing for only $x is incorrect for your purpose:
my ($x,$y, $z);
$w; # not declared (use strict will catch it and die)
$x = 0; # declared and defined BUT if you make a logic test like 'if ($x) {}' then it will be FALSE, so don't confuse testing for **'$x'** and testing for **'defined $x'**
$y = undef; # declared but not defined
$z = 1; # declared, defined, and logial test TRUE
Finally the answer of xenorraticide seems faulty to me: he suggest 'unless $x' that is not correct for testing if defined as I said before. He also suggest 'unless exists $x', that is wrong for testing scalars. 'exists' test is only for hashes keys (and deprecated for arrays).
Hope this helps.
#
print "Not defined" if !defined($x);
result will be
Not defined
#
use strict;
print "Not defined" if !defined($x);
will generate error as in your question.
Look to: http://perldoc.perl.org/strict.html, where described how you can import only required restrictions. (However use strict 'vars' is very good idea :) )
Normally this kind of code should not be required for a serious program, but still why not just for fun: (assuming use strict)
print "Not defined\n" unless eval 'ref(\$x)';
#!/usr/bin/perl -l
use strict;
# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'
our $lol = eval {$lol} || 'lol' ;
print $lol;
my solution is
#!/usr/bin/perl -l
use strict;
# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'
our $lol = eval {$lol} || 'lol' ;
print $lol;
You can use unless, like this:
use 'strict';
my $var = 'defined';
unless (defined($var)) {
print "not defined\n";
}
$var = undef;
unless (defined($var)) {
print "not defined\n";
}
The first print will not print anything, the second one will since $var has been made undef.