Running this code
parsesendnotes.pl
#!/usr/bin/perl
use strict;
use warnings;
use Device::SerialPort;
use Time::HiRes qw(usleep); # For sleep in ms
if ($#ARGV + 1 != 2) {
print "Usage: $0 port filename\n";
print "Example: $0 /dev/ttyASM0 money.txt\n";
exit 1;
}
my $file = $ARGV[0];
my $dev = $ARGV[1];
if (!-e $file || !-e $dev) {
print "File or brain not found.\n";
exit 1;
}
my $arduino = DeviceSerialPort->new($dev);
$arduino->baudrate(9600);
$arduino->databits(8);
$arduino->parity("none");
$arduino->stopbits(1);
require "frequencies.pl";
open NOTES, "$file";
print $frequencies{"LA3"};
while (<NOTES>) {
chomp; # No newline
s/#.*//; # No comments
s/^\s+//; # No leading white
s/\s+$//; # No trailing white
next unless length;
if ($_ =~ m/^TEMPO/) {
my $tempo = split(/\s+/, $_, -1);
print "Tempo is $tempo.";
} else {
my #tone = split(/\s+/, $_);
}
my $note = $frequencies{$tone[0]};
my $duration = $tone[1]*$tempo;
print "Playing $tone[0] (\#$note Hz) for $tone[1] units ($duration ms).";
while ($note > 255) {
$arduino->write(chr(255));
$note -= 255;
}
$arduino->write(chr($note));
$arduino->write(";");
usleep($duration);
}
frequencies.pl
my %frequencies = (
"PAUSE" => 0,
"B0" => 31,
"DO1" => 33,
"DOD1" => 35,
...
);
I obtain these errors
Global symbol "%frequencies" requires explicit package name at ./parsensendnotes2.pl line 30.
Global symbol "%frequencies" requires explicit package name at ./parsensendnotes2.pl line 44.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 44.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 45.
Global symbol "$tempo" requires explicit package name at ./parsensendnotes2.pl line 45.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 46.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 46.
Execution of ./parsensendnotes2.pl aborted due to compilation errors.
What am I doing wrong?
name %frequencies is localized in file frequencies.pl: my lasts until end of block, or end of file.
A better way would be to remove my and do like this:
my %frequencies;
eval { %frequencies = do "frequencies.pl"; }
# must check $! and $# here -- see perldoc -f do`
However, an even better way is to use YAML instead:
freq.yml
---
"PAUSE": 0
"B0": 31
"DO1": 33
"DOD1": 35
And then
use YAML qw(LoadFile);
# ...
my $data = LoadFile("freq.yml");
%frequencies = %$data;
As for #tone, $tempo & co, again, my variable scope is limited to {} block. You should do something like
my $x;
if (...) { $x = ... };
to make $x accessible outside if.
my %frequencies from frequencies.pl is not declared inside parsesendnotes.pl.
You need our %frequencies in your main script. And of course the same for the other variables.
Some documentation:
perldoc -f my
perldoc -f our
Related
Is there a way to get the line number (and maybe filename) where a __DATA__ token was coded? Or some other way to know the actual line number in the original source file where a line of data read from the DATA filehandle came from?
Note that $. counts from 1 when reading from the DATA filehandle. So if the line number of the __DATA__ token were added to $. it would be what I'm looking for.
For example:
#!/usr/bin/perl
while (<DATA>) {
my $n = $. + WHAT??;
die "Invalid data at line $n\n" if /bad/;
}
__DATA__
something good
something bad
I want this to say "Invalid data at line 9", not "line 2" (which is what you get if $. is used by itself).
In systems that support /proc/<pid> virtual filesystems (e.g., Linux), you can do:
# find the file where <DATA> handle is read from
my $DATA_FILE = readlink("/proc/$$/fd/" . fileno(*DATA));
# find the line where DATA begins
open my $THIS, "<", $DATA_FILE;
my #THIS = <$THIS>;
my ($DATA_LINE) = grep { $THIS[$_] =~ /^__DATA__\b/ } 0 .. $#THIS;
File don't actually have lines; they're just sequences of bytes. The OS doesn't even offer the capability of getting a line from a file, so it has no concept of line numbers.
Perl, on the other hand, does keep track of a line number for each handle. It is accessed via $..
However, the Perl handle DATA is created from a file descriptor that's already been moved to the start of the data —it's the file descriptor that Perl itself uses to load and parse the file— so there's no record of how many lines have already been read. So the line 1 of DATA is the first line after __DATA__.
To correct the line count, one must seek back to the start of the file, and read it line by line until the file handle is back at the same position it started.
#!/usr/bin/perl
use strict;
use warnings qw( all );
use Fcntl qw( SEEK_SET );
# Determines the line number at the current file position without using «$.».
# Corrects the value of «$.» and returns the line number.
# Sets «$.» to «1» and returns «undef» if unable to determine the line number.
# The handle is left pointing to the same position as when this was called, or this dies.
sub fix_line_number {
my ($fh) = #_;
( my $initial_pos = tell($fh) ) >= 0
or return undef;
seek($fh, 0, SEEK_SET)
or return undef;
$. = 1;
while (<$fh>) {
( my $pos = tell($fh) ) >= 0
or last;
if ($pos >= $initial_pos) {
if ($pos > $initial_pos) {
seek($fh, $initial_pos, SEEK_SET)
or die("Can't reset handle: $!\n");
}
return $.;
}
}
seek($fh, $initial_pos, SEEK_SET)
or die("Can't reset handle: $!\n");
$. = 1;
return undef;
}
my $prefix = fix_line_number(\*DATA) ? "" : "+";
while (<DATA>) {
printf "%s:%s: %s", __FILE__, "$prefix$.", $_;
}
__DATA__
foo
bar
baz
Output:
$ ./a.pl
./a.pl:48: foo
./a.pl:49: bar
./a.pl:50: baz
$ perl <( cat a.pl )
/dev/fd/63:+1: foo
/dev/fd/63:+2: bar
/dev/fd/63:+3: baz
Perl keeps track of the file and line at which each symbol is created. A symbol is normally created when the parser/compiler first encounters it. But if __DATA__ is encountered before DATA is otherwise created, this will create the symbol. We can take advantage of this to set the line number associated with the file handle in DATA.
For the case where the Package::DATA handle is not used in Package.pm itself, the line number of the __DATA__ token could be obtained via B::GV->LINE on the DATA handle:
$ cat Foo.pm
package Foo;
1;
__DATA__
good
bad
$ perl -I. -MFoo -MB -e '
my $ln = B::svref_2object(\*Foo::DATA)->LINE;
warn "__DATA__ at line $ln\n";
Foo::DATA->input_line_number($ln);
while(<Foo::DATA>){ die "no good" unless /good/ }
'
__DATA__ at line 4
no good at -e line 1, <DATA> line 6.
In the case where the DATA handle is referenced in the file itself, a possible kludge would be to use an #INC hook:
$ cat DH.pm
package DH;
unshift #INC, sub {
my ($sub, $fname) = #_;
for(#INC){
if(open my $fh, '<', my $fpath = "$_/$fname"){
$INC{$fname} = $fpath;
return \'', $fh, sub {
our (%ln, %pos);
if($_){ $pos{$fname} += length; ++$ln{$fname} }
}
}
}
};
$ cat Bar.pm
package Bar;
print while <DATA>;
1;
__DATA__
good
bad
$ perl -I. -MDH -MBar -e '
my $fn = "Bar.pm";
warn "__DATA__ at line $DH::ln{$fn} pos $DH::pos{$fn}\n";
seek Bar::DATA, $DH::pos{$fn}, 0;
Bar::DATA->input_line_number($DH::ln{$fn});
while (<Bar::DATA>){ die "no good" unless /good/ }
'
good
bad
__DATA__ at line 6 pos 47
no good at -e line 6, <DATA> line 8.
Just for the sake of completion, in the case where you do have control over the file, all could be easily done with:
print "$.: $_" while <DATA>;
BEGIN { our $ln = __LINE__ + 1; DATA->input_line_number($ln) }
__DATA__
...
You can also use the first B::GV solution, provided that you reference the DATA handle via an eval:
use B;
my ($ln, $data) = eval q{B::svref_2object(\*DATA)->LINE, \*DATA}; die $# if $#;
$data->input_line_number($ln);
print "$.: $_" while <$data>;
__DATA__
...
None of these solutions assumes that the source file are seekable (except if you want to read the DATA more than once, as I did in the second example), or try to reparse your files, etc.
Comparing the end of the file to itself in reverse might do what you want:
#!/usr/bin/perl
open my $f, "<", $0;
my #lines;
my #dataLines;
push #lines ,$_ while <$f>;
close $f;
push #dataLines, $_ while <DATA>;
my #revLines= reverse #lines;
my #revDataLines=reverse #dataLines;
my $count=#lines;
my $offset=0;
$offset++ while ($revLines[$offset] eq $revDataLines[$offset]);
$count-=$offset;
print "__DATA__ section is at line $count\n";
__DATA__
Hello there
"Some other __DATA__
lkjasdlkjasdfklj
ljkasdf
Running give a output of :
__DATA__ section is at line 19
The above script reads itself (using $0 for file name) into the #lines array and reads the DATA file into the #dataLines array.
The arrays are reversed and then compared element by element until they are different. The number of lines are tracked in $offset and this is subtracted from the $count variable which is the number of lines in the file.
The result is the line number the DATA section starts at. Hope that helps.
Thank you #mosvy for the clever and general idea.
Below is a consolidated solution which works anywhere. It uses a symbolic reference instead of eval to avoid mentioning "DATA" at compile time, but otherwise uses the same ideas as mosvy.
The important point is that code in a package containing __DATA__ must not refer to the DATA symbol by name so that that symbol won't be created until the compiler sees the __DATA__ token. The way to avoid mentioning DATA is to use a filehandle ref created at run-time.
# Get the DATA filehandle for a package (default: the caller's),
# fixed so that "$." provides the actual line number in the
# original source file where the last-read line of data came
# from, rather than counting from 1.
#
# In scalar context, returns the fixed filehandle.
# In list context, returns ($fh, $filename)
#
# For this to work, a package containing __DATA__ must not
# explicitly refer to the DATA symbol by name, so that the
# DATA symbol (glob) will not yet be created when the compiler
# encounters the __DATA__ token.
#
# Therefore, use the filehandle ref returned by this
# function instead of DATA!
#
sub get_DATA_fh(;$) {
my $pkg = $_[0] // caller;
# Using a symbolic reference to avoid mentioning "DATA" at
# compile time, in case we are reading our own module's __DATA__
my $fh = do{ no strict 'refs'; *{"${pkg}::DATA"} };
use B;
$fh->input_line_number( B::svref_2object(\$fh)->LINE );
wantarray ? ($fh, B::svref_2object(\$fh)->FILE) : $fh
}
Usage examples:
my $fh = get_DATA_fh; # read my own __DATA__
while (<$fh>) { print "$. : $_"; }
or
my ($fh,$fname) = get_DATA_fh("Otherpackage");
while (<$fh>) {
print " $fname line $. : $_";
}
So I have this file:
casper_mint#casper-mint-dell ~/learn_perl_hard_way $ cat bettypage
foo foo foo foo foo foo foo
boo boo boo
And wanted to read it it and print it between 2 sub routines.
This kept throwing errors:
#!/usr/bin/perl
use strict;
use warnings ;
sub read_file {
my $file = shift ;
open (FILE, $file) || die " Couldn't open $file";
while (my $line = <FILE>) {
read_line $line ;
}
}
sub read_line {
my #list = split " ", shift ;
foreach my $word (#list) {
print "$word\n";
}
}
read_file(#ARGV) ;
casper_mint#casper-mint-dell ~/learn_perl_hard_way $ ./test_hash.pl bettypage
Can't locate object method "read_line" via package "foo foo foo foo foo foo foo" (perhaps you forgot to load "foo foo foo foo foo foo foo"?) at ./test_hash.pl line 13, <FILE> line 1.
casper_mint#casper-mint-dell ~/learn_perl_hard_way $
So I put the "read_line subroutine" before the "read_file subroutine" - since it depends on it, from a procedural point of view and it works just fine.
#!/usr/bin/perl
use strict;
use warnings ;
sub read_line {
my #list = split " ", shift ;
foreach my $word (#list) {
print "$word\n";
}
}
sub read_file {
my $file = shift ;
open (FILE, $file) || die " Couldn't open $file";
while (my $line = <FILE>) {
read_line $line ;
}
}
read_file(#ARGV) ;
I know from working with bash that the subroutines usually has to come first in the code for it to work.
However, I thought that perl compiles the script and then executes it. And by compiling, I did not think that it would matter where the subroutine was located.
That by compiling everything before executing the subroutine would at least be available to be read it by the whole program. If perl compiles the whole script before executing it, why should the order of the subroutine matter - shouldn't the "read_line" subroutine be available to the "read_file" subroutine - regardless of where it is placed in the script?
Unless predeclared, you need to call your subs with parenthesis, ie. read_line($line)
From perlsub
To call subroutines:
1. NAME(LIST); # & is optional with parentheses.
2. NAME LIST; # Parentheses optional if predeclared/imported.
3. &NAME(LIST); # Circumvent prototypes.
4. &NAME; # Makes current #_ visible to called subroutine.
But really, just get into the habit of always using parenthesis (option 1). Your code will thank you later with better readability and less surprises.
Can somebody explain why my loop does not work?
#!/usr/bin/perl -w
use warnings;
use strict;
use URI;
use Web::Scraper;
my $url = "http://example.com";
# prepare data
my $scrapedata = scraper {
process "div.something", 'pages[]' => '#rel';
};
# scrape the data
my $res = $scrapedata->scrape(URI->new($url));
# Get number of pages and define as var
for my $j (0 .. $#{$res->{pages}}) {
my $varpages = $res->{pages}[$j];
print "$varpages\n";
}
for ( my $count = 2; $count <= $varpages; $count++) {
print "$varpages\n";
print "$count\n";
}
This is the error :
# perl oli
Global symbol "$varpages" requires explicit package name at oli line 25.
Global symbol "$varpages" requires explicit package name at oli line 27.
Execution of oli aborted due to compilation errors.
$varpages is lexically scoped to the code block:
# Get number of pages and define as var
for my $j (0 .. $#{$res->{pages}}) {
my $varpages = $res->{pages}[$j];
print "$varpages\n";
}
The attempt to reference it later references a different variable. If you want $varpages to have global scope, declare it outside the block that assigns to it. For example:
my $varpages;
# Get number of pages and define as var
for my $j (0 .. $#{$res->{pages}}) {
$varpages = $res->{pages}[$j];
print "$varpages\n";
}
I'm guessing this should be something obvious to those knowing Perl, but I simply don't get it... I also guess it has to do with problems described in Perl scoping « darkness - but I cannot apply any of that in my case.
Anyway, here's the code:
#!/usr/bin/env perl
# call with:
# ./test.pl
use strict;
my $tvars = "my \$varA = 1;
my \$varB = 2;
my \$varC = 3;
";
my #lines = split /\n/, $tvars;
foreach my $line (#lines) {
print "$line\n";
eval $line; warn $# if $#;
}
#~ print "$varA\n"; # Global symbol "$varA" requires explicit package name at ./test.pl line 18.
#~ print "$varB\n"; # Global symbol "$varB" requires explicit package name at ./test.pl line 19.
#~ print "$varC\n"; # Global symbol "$varC" requires explicit package name at ./test.pl line 20.
$tvars = "our \$varA = 1;
our \$varB = 2;
our \$varC = 3;
";
#lines = split /\n/, $tvars;
foreach my $line (#lines) {
print "$line\n";
eval $line; warn $# if $#;
}
print "$varA\n"; # Global symbol "$varA" requires explicit package name at ./test.pl line 33.
print "$varB\n"; # Global symbol "$varB" requires explicit package name at ./test.pl line 34.
print "$varC\n"; # Global symbol "$varC" requires explicit package name at ./test.pl line 35.
Simply speaking, I'd like to have something like "$varA = 1;" written as a string (text file); and I'd like perl to eval it, so that afterwards I have access to variable "$varA" in the same script - the errors I get when I try to access those after an eval are in the comments of the code above (however, no warnings are reported during the eval). (I'm guessing, what I'd need is something like "global" variables, if the eval runs in a different context than the main script?)
How would I go about doing that? Do I have to go through all of that package definition business, even for a simple script like the above?
It has everything to do with scoping. The variables are declared with my inside the eval expression. This makes them local to the eval statement and not accessible once the eval statement exits. You can declare them first, though:
my ($varA, $varB, $varC); # declare outside the eval statement
my $tvars = "\$varA = 1;
\$varB = 2;
\$varC = 3;
";
eval $tvars;
# local $varA, $varB, $varC variables are now initialized
or as you suggest, you can use global variables. The easiest (though not necessarily the "best" way) is to prepend :: to all variable names and get them in the main package.
my $tvars = "\$::varA = 1;
\$::varB = 2;
\$::varC = 3;
";
eval $tvars;
print "A=$::varA, B=$::varB, C=$::varC\n";
Now when you tried our variables in your example, you actually were initializing package (global) variables. But outside the eval statement, you still need to qualify (i.e., specify the package name) them in order to access them:
$tvar = "our \$foo = 5";
eval $tvar;
print $main::foo; # ==> 5
The problem is that when you do eval $string, $string is evaluated as its own subroutine which has its own lexical scope. From perldoc -f eval:
In the first form [in which the argument is a string], the return value of EXPR is parsed and
executed as if it were a little Perl program. The value of the expression (which is itself
determined within scalar context) is first parsed, and if there were no errors, executed in the
lexical context of the current Perl program, so that any variable settings or subroutine and format
definitions remain afterwards.
So, in other words, if you have:
use strict;
use warnings;
eval "my $foo=5;";
print "$foo\n";
you'll get an error:
Global symbol "$foo" requires explicit package name at -e line 3.
Global symbol "$foo" requires explicit package name at -e line 4.
However, if you initialize your variables first, you're fine.
use strict;
use warnings;
my $foo;
eval "\$foo=5;";
print "$foo\n"; #prints out 5, as expected.
I've been learning Perl and whenever I write a non-trivial script I always get this error message. I always think I have a good understanding of it, but I suppose I don't. Here is a sloppy markov chain example (not tested) with the errors below.
The
#!/usr/bin/perl -w
use strict;
sub croak { die "$0: #_: $!\n"; }
sub output {
my %chains = shift;
my #keys = keys %chains;
my $index = rand($keys);
my $key = $keys[$index];
my $out_buf = $key;
for (my $i = 0; $i < 100; ++$i) {
my $aref = $chains{$key};
my $word = #$aref[rand($aref)];
$out_buf .= " $word";
$key =~ s/.+ //;
$key .= " $word";
}
print $out_buf, "\n";
}
sub get_chains {
my %chains;
my #prefixes
while (my $line = <FILE>) {
my #words = split " ", $line;
foreach my $word (#words) {
if ($prefixes == 2) {
my $key = join " ", #prefixes;
my $arr_ref = $chains{$key};
push(#$arr_ref, $word);
shift #prefixes;
}
push(#prefixes, $word);
}
}
return %chains;
}
sub load_book {
my $path_name = shift #ARGV;
open(FILE, $path_name) || croak "File not found.\n";
}
load_book;
my %chains = get_chains;
output %chains;
----ERRORS----
"my" variable $line masks earlier declaration in same statement at markov.pl line 33.
"my" variable $path_name masks earlier declaration in same scope at markov.pl line 55.
Global symbol "$keys" requires explicit package name at markov.pl line 12.
syntax error at markov.pl line 32, near ") {"
Global symbol "$prefixes" requires explicit package name at markov.pl line 36.
Global symbol "%chains" requires explicit package name at markov.pl line 48.
syntax error at markov.pl line 49, near "}"
syntax error at markov.pl line 56, near "}"
Execution of markov.pl aborted due to compilation errors.
What mistake(s) am I making?
There are three syntax errors in your script:
Global symbol "$keys" requires explicit package name at markov.pl line 12.
You didn't declare $keys, and because of "use strict", that is a fatal error.
You probably meant:
my $index = rand(#keys);
The second error:
Global symbol "$prefixes" requires explicit package name at markov.pl line 36.
is the same thing: you meant:
if (#prefixes == 2) {
Finally, in line 30, you're missing a semicolon after:
my #prefixes
This confuses the parser, and causes all the other errors and warnings.
You may want to read the perldata documentation if you're unclear about the use of sigils ($, #, %).