There is something I don't understand :
say in is a file containing :
1
2
3
and foo.pl :
use strict;
<>;
print;
<>;
print;
<>;
print;
and then run :
perl foo.pl < in
Why this programm doesn't output anything ?
...While this one :
use strinct;
while(<>) {
print;
}
output the whole file
Because
while(<>)
Is shorthand for
while($_ = <>)
Which means the line is assigned to the default variable $_. Which is also used by print.
What you wrote:
<>;
Does not assign anything to $_. It is just a readline in void context, which means the value is discarded and not stored anywhere. Hence $_ is empty. If you use warnings, Perl will tell you what is going on.
Use of uninitialized value $_ in print
If you do the assignment manually it will work:
$_ = <>;
Note also that you do not have to redirect the file content, you can just supply the file name as an argument:
perl foo.pl in
Related
I have simple question:
why the first code does not print the first line of the file but the second one does?
#! /usr/bin/perl
use warnings;
use strict;
my $protfile = "file.txt";
open (FH, $protfile);
while (<FH>) {
print (<FH>);
}
#! /usr/bin/perl
use warnings;
use strict;
my $protfile = "file.txt";
open (FH, $protfile);
while (my $file = <FH>) {
print ("$file");
}
Context.
Your first program tests for end-of-file on FH by reading the first line, then reads FH in list context as an argument to print. That translates to the whole file, as a list with one line per item. It then tests for EOF again, most likely detects it, and stops.
Your second program iterates by line, each one read in scalar context to variable $file, and prints them individually. It detects EOF by a special case in the while syntax. (see the code samples in the documentation)
So the specific reason why your program doesn't print the first line in one case is that it's lost in the argument to while. Do note that the two programs' structure is pretty different: the first only runs a single while iteration, while the second iterates once per line.
PS: nowadays, the recommended way to manage files tends towards lexical filehandles (open my $file, 'name'; print <$file>;).
Because you are comsuming the first line with the <> operator and then using it again in the print, so the first line has already gone but you are not printing it. <> is the readline operator. You need to print the $_ variable, or assign it to a defined variable as you are doing in the second code. You could rewrite the first:
print;
And it would work, because print uses $_ if you don't give it anything.
When used in scalar context, <FH> returns the next single line from the file.
When used in list context, <FH> returns a list of all remaining lines in the file.
while (my $file = <FH>) is a scalar context, since you're assigning to a scalar. while (<FH>) is short for while(defined($_ = <FH>)), so it is also a scalar context. print (<FH>); makes it a list context, since you're using it as argument to a function that can take multiple arguments.
while (<FH>) {
print (<FH>);
}
The while part reads the first line into $_ (which is never used again). Then the print part reads the rest of the lines all at once, then prints them all out again. Then the while condition is checked again, but since there are now no lines left, <FH> returns undef and the loop quits after just one iteration.
while (my $file = <FH>) {
print ("$file");
}
does more what you probably expect: reads and then prints one line during each iteration of the loop.
By the way, print $file; does the same as print ("$file");
while (<FH>) {
print (<FH>);
}
use this instead:
while (<FH>) {
print $_;
}
I get the below error when i run the script: Could someone help me on this
Use of uninitialized value $user in scalar chomp at ./temp.pl line 38, <DATA> line 558.
Use of uninitialized value $modelName in scalar chomp at ./temp.pl line 39, <DATA> line 558.
Use of uninitialized value $Application in scalar chomp at ./temp.pl line 40, <DATA> line 558.
Use of uninitialized value $user in string eq at ./temp.pl line 42, <DATA> line 558.
The code is as below, could some-one help me on this:
my ($user) = $ARGV[0];
my ($modelName) = $ARGV[1];
my ($Application) = $ARGV[2];
chomp($user);
chomp($modelName);
chomp($Application);
if ( ($user eq "") || ($modelName eq "") || ($Application eq "")) {
&usage;
}
sub usage {
print "\tUsage : $0 User ModelName Application\n";
exit (1);
}
The program is expecting parameters - user, model name, and application - on the command line and you have provided none
There is no need to chomp a value passed from the command line as it will never end in a new line
Your code is better written like this
usage() unless #ARGV == 3;
my ($user, $modelName, $Application) = #ARGV;
You are suppose to input three parameters on the command line when you run your program. However, there is no check to see if you do so in your program. Thus, if there is no first parameter, $ARGV[0] is null, and so is $user. When you chomp user, you get an error. Here's a slightly modified version of your program:
use strict;
use warnings;
# Let's give some directions
my $usage <<USAGE;
Usage: <program> <user> <model> <application>
All parameters are required!
USAGE
if (scalar #ARGV != 3) {
die "$usage";
}
my $user = shift;
my $modelName = shift;
my $Application = shift;
# No need for this
chomp $user;
chomp $modelName;
chomp $Application;
Notice a few things:
I have use strict and use warnings pragmas. Always use these in all of your programs. These help spot about 90% of errors that people make in programs.
I check whether the arguments are there before I do anything else. I use scalar to force #ARGV into giving me its scalar value (which it would do anyway in this context), and see if there are actually three and only three values in it. If there aren't three values, I give the usage text.
You don't need to chomp the command line parameters. Chomping is only really needed on file input because each file line is read in with its EOL character or characters. This is mainly done to ensure that the <...> doesn't return a zero if the line is merely blank. If the line is blank, you'll get <...> returning at least a single NL character.
One more thing...
I like to use a $usage text rather than a subroutine. I set the usage text right on the very top of my program where someone who looks at my code can see it, and read exactly how the program is used. I can also put my usage text in a die statement.
Plus, it's bad form to print anything out in a subroutine. Subroutines should return values which the calling program can operate on.
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 have some code that looks like
my ($ids,$nIds);
while (<myFile>){
chomp;
$ids.= $_ . " ";
$nIds++;
}
This should concatenate every line in my myFile, and nIds should be my number of lines. How do I print out my $ids and $nIds?
I tried simply print $ids, but Perl complains.
my ($ids, $nIds)
is a list, right? With two elements?
print "Number of lines: $nids\n";
print "Content: $ids\n";
How did Perl complain? print $ids should work, though you probably want a newline at the end, either explicitly with print as above or implicitly by using say or -l/$\.
If you want to interpolate a variable in a string and have something immediately after it that would looks like part of the variable but isn't, enclose the variable name in {}:
print "foo${ids}bar";
You should always include all relevant code when asking a question. In this case, the print statement that is the center of your question. The print statement is probably the most crucial piece of information. The second most crucial piece of information is the error, which you also did not include. Next time, include both of those.
print $ids should be a fairly hard statement to mess up, but it is possible. Possible reasons:
$ids is undefined. Gives the warning undefined value in print
$ids is out of scope. With use
strict, gives fatal warning Global
variable $ids needs explicit package
name, and otherwise the undefined
warning from above.
You forgot a semi-colon at the end of
the line.
You tried to do print $ids $nIds,
in which case perl thinks that $ids
is supposed to be a filehandle, and
you get an error such as print to
unopened filehandle.
Explanations
1: Should not happen. It might happen if you do something like this (assuming you are not using strict):
my $var;
while (<>) {
$Var .= $_;
}
print $var;
Gives the warning for undefined value, because $Var and $var are two different variables.
2: Might happen, if you do something like this:
if ($something) {
my $var = "something happened!";
}
print $var;
my declares the variable inside the current block. Outside the block, it is out of scope.
3: Simple enough, common mistake, easily fixed. Easier to spot with use warnings.
4: Also a common mistake. There are a number of ways to correctly print two variables in the same print statement:
print "$var1 $var2"; # concatenation inside a double quoted string
print $var1 . $var2; # concatenation
print $var1, $var2; # supplying print with a list of args
Lastly, some perl magic tips for you:
use strict;
use warnings;
# open with explicit direction '<', check the return value
# to make sure open succeeded. Using a lexical filehandle.
open my $fh, '<', 'file.txt' or die $!;
# read the whole file into an array and
# chomp all the lines at once
chomp(my #file = <$fh>);
close $fh;
my $ids = join(' ', #file);
my $nIds = scalar #file;
print "Number of lines: $nIds\n";
print "Text:\n$ids\n";
Reading the whole file into an array is suitable for small files only, otherwise it uses a lot of memory. Usually, line-by-line is preferred.
Variations:
print "#file" is equivalent to
$ids = join(' ',#file); print $ids;
$#file will return the last index
in #file. Since arrays usually start at 0,
$#file + 1 is equivalent to scalar #file.
You can also do:
my $ids;
do {
local $/;
$ids = <$fh>;
}
By temporarily "turning off" $/, the input record separator, i.e. newline, you will make <$fh> return the entire file. What <$fh> really does is read until it finds $/, then return that string. Note that this will preserve the newlines in $ids.
Line-by-line solution:
open my $fh, '<', 'file.txt' or die $!; # btw, $! contains the most recent error
my $ids;
while (<$fh>) {
chomp;
$ids .= "$_ "; # concatenate with string
}
my $nIds = $.; # $. is Current line number for the last filehandle accessed.
How do I print out my $ids and $nIds?
print "$ids\n";
print "$nIds\n";
I tried simply print $ids, but Perl complains.
Complains about what? Uninitialised value? Perhaps your loop was never entered due to an error opening the file. Be sure to check if open returned an error, and make sure you are using use strict; use warnings;.
my ($ids, $nIds) is a list, right? With two elements?
It's a (very special) function call. $ids,$nIds is a list with two elements.
I am new to Perl, but I know a little bit of C though. I came across this snippet in one of our classroom notes:
$STUFF = "C:/scripts/stuff.txt";
open STUFF or die "Cannot open $STUFF for read: $!";
print "Line $. is: $_" while (<STUFF>);
Why is the while after the print statement? What does it do?
It's the same as
while (<STUFF>) {
print "Line $. is : $_";
}
It's written the way it is because it's simpler and more compact. This is, in general, Perl's (in)famous "statement modifier" form for conditionals and loops.
The other answers have explained the statement modifier form of the while loop. However, there's a lot of other magic going on here. In particular, the script relies on three of Perl's special variables. Two of these ($_ and $!) are very common; the other ($.) is reasonably common. But they're all worth knowing.
When you run while <$fh> on an opened filehandle, Perl automagically runs through the file, line by line, until it hits EOF. Within each loop, the current line is set to $_ without you doing anything. So these two are the same:
while (<$fh>) { # something }
while (defined($_ = <$fh>)) { # something }
See perldoc perlop, the section on I/O operators. (Some people find this too magical, so they use while (my $line = <$fh>) instead. This gives you $line for each line rather than $_, which is a clearer variable name, but it requires more typing. To each his or her own.)
$! holds the value of a system error (if one is set). See perldoc perlvar, the section on $OS_ERROR, for more on how and when to use this.
$. holds a line number. See perldoc perlvar, the section on $NR. This variable can be surprisingly tricky. It won't necessarily have the line number of the file you are currently reading. An example:
#!/usr/bin/env perl
use strict;
use warnings;
while (<>) {
print "$ARGV: $.\n";
}
If you save this as lines and run it as perl lines file1 file2 file3, then Perl will count lines straight through file1, file2 and file3. You can see that Perl knows what file it's reading from (it's in $ARGV; the filenames will be correct), but it doesn't reset line numbering automatically for you at the end of each file. I mention this since I was bit by this behavior more than once until I finally got it through my (thick) skull. You can reset the numbering to track individual files this way:
#!/usr/bin/env perl
use strict;
use warnings;
while (<>) {
print "$ARGV: $.\n";
}
continue {
close ARGV if eof;
}
You should also check out the strict and warnings pragmas and take a look at the newer, three-argument form of open. I just noticed that you are "unknown (google)", which means you are likely never to return. I guess I got my typing practice for the day, at least.
The following snippets are exactly equivalent, just different ways of writing the same thing:
print "Line $. is : $_" while (<STUFF>);
while (<STUFF>) {
print "Line $. is : $_";
}
What this does is each iteration through the loop, Perl reads one line of text from the STUFF file and puts it in the special variable $_ (this is what the angle brackets do). Then the body of the loop prints lines like:
Line 1 is : test
Line 2 is : file
The special variable $. is the line number of the last line read from a file, and $_ is the contents of that line as set by the angle bracket operator.
Placing the while after the print, makes the line read almost like normal English.
It also puts emphasis on the print instead of the while. And you don't need the curly brackets: { ... }
It can also be used with if and unless, for example,
print "Debug: foobar=$foobar\n" if $DEBUG;
print "Load file...\n" unless $QUIET;
I've taken the liberty of rewriting your snippet as I would.
Below my suggested code is a rogues gallery of less than optimal methods you might see in the wild.
use strict;
use warnings;
my $stuff_path = 'c:/scripts/stuff.txt';
open (my $stuff, '<', $stuff_path)
or die "Cannot open'$stuff_path' for read : $!\n";
# My preferred method to loop over a file line by line:
# while loop with explicit variable
while( my $line = <$stuff> ) {
print "Line $. is : $line\n";
}
Here are other methods you might see. Each one could be substituted for the while loop in my example above.
# while statement modifier
# - OK, but less clear than explicit code above.
print "Line $. is : $_" while <$stuff>;
# while loop
# - OK, but if I am operating on $_ explicitly, I prefer to use an explicit variable.
while( <$stuff> ) {
print "Line $. is : $_";
}
# for statement modifier
# - inefficient
# - loads whole file into memory
print "Line $. is : $_" for <$stuff>;
# for loop - inefficient
# - loads whole file into memory;
for( <$stuff> ) {
print "Line $. is : $_\n";
}
# for loop with explicit variable
# - inefficient
# - loads whole file into memory;
for my $line ( <$stuff> ) {
print "Line $. is : $line\n";
}
# Exotica -
# map and print
# - inefficient
# - loads whole file into memory
# - stores complete output in memory
print map "Line $. is : $_\n", <$stuff>;
# Using readline rather than <>
# - Alright, but overly verbose
while( defined (my $line = readline($stuff) ) {
print "Line $. is : $line\n";
}
# Using IO::Handle methods on a lexical filehandle
# - Alright, but overly verbose
use IO::Handle;
while( defined (my $line = $stuff->readline) ) {
print "Line $. is : $line\n";
}
Note that the while statement can only follow your loop body if it's a one-liner. If your loop body runs over several lines, then your while has to precede it.
It is the same as a:
while (<STUFF>) { print "Line $. is: $_"; }