Perl - Using a Variable that is inside an if statement - perl

I've been putting together a Perl script that defines a variable, and then via an if statement will assign a new value to the variable.
I want to use the last assigned value from the if statement and reference it somewhere else outside the if altogether.
It appears that when referencing it that it uses the original value.
Here's the code:
## VARIABLES
my $FILENAME = $input[0];
open my $info, $DATAFILE or die "can't open <$DATAFILE> for reading $!";
{
while (my $line = <$info>) {
chomp $line;
my #input = split(':', $line);
chomp(#input);
$FILENAME = $input[0];
$PERMIT = $input[1];
$FILESIZE = -s "$TIFF_DL_LOCATION/$PERMIT\_$FILENAME";
$SHORT_PERMIT = substr($PERMIT, 0, 2);
### DEBUG ONLY ###
print "$FILENAME / $PERMIT / $FTPBASE/$SHORT_PERMIT/$PERMIT/$FILENAME\n";
my $ftp = Net::FTP::Throttle->new(
"example.com",
MegabitsPerSecond => $THROTTLELVL,
Debug => $DEBUGLVL
) or die "Cannot connect: $#";
$ftp->login("anonymous", 'anonymous') or die "Cannot login ", $ftp->message;
$ftp->binary or die "Unable to set binary mode ", $ftp->message;
if ($PROGRESSBAR eq 1) {
print "\n[$./$LINE_COUNT] Downloading $FILENAME\n";
my $REMOTE_FILESIZE = $ftp->size("/PATH/TO/DATA/$SHORT_PERMIT/$PERMIT/$FILENAME");
if (!defined($REMOTE_FILESIZE)) {
print "=> FILE DOES NOT APPEAR TO EXIST ON FTP SERVER\n";
if ($FILENAME =~ m/_\s/) {
print "=> ATTEMPTING TO FIX NOW\n";
$FILENAME =~ s/_\s/, /g;
$REMOTE_FILESIZE = $ftp->size("/PATH/TO/DATA/$SHORT_PERMIT/$PERMIT/$FILENAME");
if (!defined($REMOTE_FILESIZE)) {
print "=> FAILED!\n";
}
}
elsif ($FILENAME =~ m/_\s\s/) {
print "=> ATTEMPTING TO FIX NOW\n";
$FILENAME =~ s/_\s\s/, /g;
$REMOTE_FILESIZE = $ftp->size("/PATH/TO/DATA/$SHORT_PERMIT/$PERMIT/$FILENAME");
if (!defined($REMOTE_FILESIZE)) {
print "$FILENAME\n";
print "=> FAILED!\n";
}
}
else {
print "=> ALL ATTEMPTS TO RESOLVE THE ISSUE HAVE FAILED.\n";
next;
}
}
$REMOTE_FILESIZE = $ftp->size("/PATH/TO/DATA/$SHORT_PERMIT/$PERMIT/$FILENAME");
print "FILENAME: $FILENAME\n";
--- SNIP SNIP - MORE DATA, NOT RELEVANT--
The output I get is the name of the file that was originally opened, not the value after modification by the substitutions in the if statements.
This is error correction: it checks the filename that it gets from the file, and if it matches something, it corrects it, and in the end, I have an old variable $FILENAME with a new value that I want to use outside the if.

I struggled to understand the code in your question, mainly because of all the upper case strings; both identifiers and comments
Local identifiers in most languages, including Perl, are usually written using lower-case letters, digits, and the underscore _. Capital letters are reserved for global variables, and in Perl's case that is mostly package (class) names
Identifiers of important variables use capital letters so that they stand out from the rest; but in your example most identifiers are all-capitals. It is diffifult to understand the structure of your code if every identifier says it's very important
I have looked carefully at the code you show, and have done my best to reorganise and simplify it so that it is more legible and does what I think you intended. I have added declarations for all of the variables that are not declared within your code sample, and have added what I think are the correct closing braces } to balance the syntax so that it will compile
As others have noted, the second conditional clause that tests for underscore followed by two spaces will never be executed, because the preceding test for underscore followed by one space will already have caught that case. It is also pointless to use a separate pattern match to determine whether a substitution s/// will succeed, as the substitution alone does nothing and returns a false value if there was no match
The intention is to help you to write Perl code that you can understand, as well as others who you may ask for help (like Stack Overflow) or may be given the job of maintaining your software.
use strict;
use warnings;
my ($progressbar, $line_count);
my ($datafile, $ftpbase, $short, $tiff_dl_location);
my ($throttlelvl, $debuglvl);
my #input;
## Variables
my $filename = $input[0];
open my $info, $datafile or die "can't open <$datafile> for reading $!";
{
while (<$info>) {
chomp;
my ($filename, $permit) = split /:/;
my $filesize = -s "$tiff_dl_location/${permit}_${filename}";
my $short_permit = substr $permit, 0, 2;
### DEBUG ONLY ###
print "$filename / $permit / $ftpbase/$short_permit/$permit/$filename\n";
my $file_path = "/PATH/TO/DATA/$short_permit/$permit/$filename";
my $ftp = Net::FTP::Throttle->new(
"example.com",
MegabitsPerSecond => $throttlelvl,
Debug => $debuglvl
) or die "Cannot connect: $#";
$ftp->login(qw/ anonymous anonymous /) or die "Cannot login: ", $ftp->message;
$ftp->binary or die "Unable to set binary mode ", $ftp->message;
if ($progressbar == 1) {
print "\n[$./$line_count] Downloading $filename\n";
my $remote_filesize = $ftp->size($file_path);
if (not defined $remote_filesize) {
warn "=> File does not appear to exist on FTP server\n";
warn "=> Attempting to fix now\n";
$filename =~ s/_\s+/, /g;
$remote_filesize = $ftp->size($file_path);
if (not defined $remote_filesize) {
warn "=> ALL ATTEMPTS TO RESOLVE THE ISSUE HAVE FAILED.\n";
next;
}
}
print "File name: $filename\n";
# --- snip snip - more data, not relevant ---
}
}
}

Declare variables outside of (before) the conditional unless they will only be used inside of that conditional block {}.
Also,
use strict;
use warnings;

Related

Perl File Write Issue

I'm having a really weird problem with this perl script. The basic point is that sometimes a file write/append doesn't happen. On a run of the program, either all of the writes will happen or none of them will. Here is the subroutine, with some comments:
sub process_svs {
my $F;
open($F, '<', $_[0]);
if($log_dups==1) {
open($dfh, '>>',"./duplicates.txt");
}
while (my $line = <$F>) {
chomp $line;
if($line =~ /somepattern/) {
if (! -e "somefile") {
copy("source","dest") or warn ("couldn't copy");
} elsif($log_dups==1) {
system("touch ./duplicates.txt"); # ghetto workaround
print $dfh "./".$_[0]."_files/".$1.",v already exists\n" or die("Couldn't write duplicate"); # problem line
}
}
}
close $F;
}
The print statements to stdout always work, but if I remove the touch ./duplicates.txt crap, nothing is written to duplicates.txt.
The other "weird" thing, is that earlier in the program, I create a directory with perl mkdir, and if the directory exists when the program is run, I don't need the workaround, the duplicates.txt writing works just fine. If I delete the directory, and let the program mkdir it, it doesn't work. Seems relevant, but I can't figure out how since the directory and the text file are not in the same location, or related in any way, that I can think of.
Additionally, I have run it through the debugger, and can see the write call being executed, but inspecting duplicates.txt immediately after the write shows nothing written.
Any possible reasons for this would be greatly appreciated.
If you want to see a modified, but more complete, version of the script, it is here:
use strict;
use warnings;
use File::Copy;
my $svs = $ARGV[0];
my $rhis_str = system("rhis $svs > ./tmp_history");
my $fh;
my $dfh;
my #versions;
my $all_revs = 0;
my $current_rev = "";
my $log_dups = 0;
sub process_svs {
my $F;
open($F, '<', $_[0]);
if($log_dups==1) {
open($dfh, '>>',"./duplicates.txt");
}
while (my $line = <$F>) {
chomp $line;
if($line =~ /something/) {
if (! -e "something") {
copy("source","dest") or warn ("couldn't copy ");
} elsif($log_dups==1) {
system("touch ./duplicates.txt"); # ghetto workaround
print $dfh "something already exists\n" or die("Couldn't write duplicate");
}
}
}
close $F;
}
for(my $i = 0; $i <= scalar #ARGV; $i++) {
my $arg = $ARGV[$i];
if($arg eq "-a") {
$all_revs = 1;
} elsif($arg eq "-r") {
$all_revs = 0;
$current_rev = $ARGV[$i+1];
} elsif($arg eq "--log-dups") {
$log_dups = 1;
}
}
open($fh, '<','./tmp_history') or die(">>> Failed to open ./tmp_history");;
mkdir "./".$svs."_files";
if($all_revs == 1) {
print ">>> Processing all revisions of ".$svs;
if($log_dups==1) {
print" (and logging duplicates)\n";
}
while(my $line = <$fh>) {
chomp $line;
if ($line =~ /something/) {
push #versions, $1;
}
}
}
system("some_cmd &>/dev/null");
process_svs($svs);
}
You're not checking to see if your files opened. This is a very, very basic mistake and you should fix this immediately. Either add or die $! after each open or, better yet, use autodie and it will take care of catching all IO exceptions for you and give you good, consistent error messages.
Most importantly, this will tell you why it failed to open. $! tells you why it failed. You don't have that in your check on print.
print $dfh "./".$_[0]."_files/".$1.",v already exists\n" or die("Couldn't write duplicate"); # problem line
You're checking if print failed, but you're not including $!. Either add $! like die "Couldn't write to duplicate: $!" or use autodie, remove the or die clause, and let autodie take care of it. I recommend the second.
I suspect you'll find that something else is deleting duplicates.txt between the open and the print.
The second thing that grabs my attention is here.
if($log_dups==1) {
open($dfh, '>>',"./duplicates.txt");
}
You're using a global variable $log_dups to decide whether or not to open the file for writing (and not checking if it succeeded). This should be a variable that gets passed into the function, it's just good programming practice. Later you decide whether to print to $dfh based on that global variable.
if (! -e "something") {
copy("source","dest") or warn ("couldn't copy ");
} elsif($log_dups==1) {
system("touch ./duplicates.txt"); # ghetto workaround
print $dfh "something already exists\n" or die("Couldn't write duplicate");
}
Because $log_dups is global it's possible something else is changing $log_dups between deciding to open duplicates.txt and writing to it. To avoid all these problems, and to make the code simpler, $log_dups should be an argument passed into the function.
Furthermore, the filehandle $dfh is inexplicably a global. Same problem, something else could be closing it. It will also not be automatically closed at the end of the function which might leave writes to duplicates.txt buffered until the program exits. $dfh should be a lexical.
Other problems...
my $rhis_str = system("rhis $svs > ./tmp_history");
$rhis_str will contain the exit status of the rhis program. I don't think that's what you want. You don't use this variable anyway.
There's no need to pass ./file to open, it's safe and easier to read to use just pass file. That it's in the current working directory is implied.
If you fix these basic problems and still have trouble, then edit your question with the revised code and we can look again.

Pattern matching in perl & assigning

I need to search for a particular string in a file and then assign it to a variable, example: in the file content it is written as CURRENT_RUN_ID=1636, so I need to search for string CURRENT_RUN_ID and assign its given value i.e. 1636 toa variable sayrunvar`, for this I tried below given, but it doesn't seem to be working, can you correct me here?
opendir(DAPATH,$sDAPATH) or die "Can't open $sDAPATH: $!";
print OUTLOG "\nfound da path : $sDAPATH\n";
my #adirs = readdir(DAPATH);
print OUTLOG "Starting capturing DA\n";
my $da = glob "*$runVar.csv*";
print OUTLOG "Assigned DA";
closedir(DAPATH);
}
It is not matching because you are giving spaces at if (/CURRENT_RUN_ID = \s*(.*)/) in the match.It is searching for spaces in the string.
The pattern what you are trying to match will match CURRENT_RUN_ID = 1636 string, notice the spaces between CURRENT_RUN_ID and = and after =. If there is match then the no of spaces in the pattern should be exactly same as the no of spaces in the string.
There is space between CURRENT_RUN_ID and = and also after =.
Better remove the faulty spaces and make the space optional using \s* try this:
if (/CURRENT_RUN_ID\s*=\s*(.*)/){
my $runvar = $1;
print "$runvar \n";
}
EDIT:
As per your requirement I changed your script as(I am not writing to file):
#!/usr/bin/perl
use strict;
use warnings;
open my $fh, '<', 'file' or die "unable to open file : $! \n";
my $runVar="";
while(<$fh>){
if (/CURRENT_RUN_ID\s*=\s*(.*)/){
print "we can now assign run id\n";
$runVar = $1;
print "assigned current run id to variable\n";
}
else {
print "run id not assigned\n";
}
}
close($fh);
Here you are not matching any of the lines while performing the pattern matching in line 18. And even you can reduce few repetitive steps in the code.
But for your convenience I have used your program.
#!C:\Strawberry\perl\bin
use strict;
use warnings;
my $sSuccessString = "CURRENT_RUN_ID";
open(LOG,"$slogfile") or die("Can't open $slogfile\n");
my $sLines;
{
local $/ = undef;
$sLines=<LOG>;
}
if($sLines =~ m/$sSuccessString/){
open(OUTLOG, ">>test.txt");
print OUTLOG "found current run id in log\n";
print OUTLOG "it is found in log as : $sSuccessString \n";
#if ($sLines =~ m/(CURRENT_RUN_ID=.*)/i) {
#print OUTLOG "<p>" . $1 . "<\/p>\n";
#In below line you need to match the pattern with the line.
if ($sLines=~/CURRENT_RUN_ID=(.*)/){
print OUTLOG "we can now assign run id\n";
my $runVar = $1;
print "$runVar\n";
print OUTLOG "assigned current run id to variable\n";
}
else {
print "run id not assigned\n";
}
}
I have copied your log file content to one of my text file (prog.txt) on my desktop and ran the script. Please see the output.
#!C:\Strawberry\perl\bin
use strict;
use warnings;
my $sSuccessString = "CURRENT_RUN_ID";
open(LOG,"Prog.txt") or die("Can't open text file\n");
my $sLines;
{
local $/ = undef;
$sLines=<LOG>;
}
if($sLines =~ m/$sSuccessString/){
open(OUTLOG, ">>test.txt");
print OUTLOG "found current run id in log\n";
print OUTLOG "it is found in log as : $sSuccessString \n";
#if ($sLines =~ m/(CURRENT_RUN_ID=.*)/i) {
#print OUTLOG "<p>" . $1 . "<\/p>\n";
if ($sLines=~/CURRENT_RUN_ID=(.*)/){
print OUTLOG "we can now assign run id\n";
my $runVar = $1;
print "$runVar\n";
print OUTLOG "assigned current run id to variable\n";
}
else {
print "run id not assigned\n";
}
}
OUTPUT:
C:\Users\hclabv\Desktop>perl run1.pl
1637
And my log file (test.txt) where we are capturing the logs while running script is as below.
found current run id in log
it is found in log as : CURRENT_RUN_ID
we can now assign run id
assigned current run id to variable
For getting runVar.csv file from desired directory, I have added the below piece of code with in the if loop
opendir(DAPATH,"C:/Users/hclabv/Desktop/Scripting/files") or die "Can't open DAPATH: $!";
print OUTLOG "\nfound da path : DAPATH\n";
my #adirs = readdir(DAPATH);
print "csv files #adirs\n";
print OUTLOG "Starting capturing DA\n";
my $csvfile = "$runVar.csv";
foreach my $adirs (#adirs) {
if ($adirs eq $csvfile) {
print "file found\n";
my $da = $adirs;
print "csv file is $da\n";
}
print OUTLOG "Assigned DA";
close(DAPATH);
}

Reading data from file and creating a file and writing data is the file is not found

I have come into a situation where I need to read 2 lines data (username and password) from a file called login.txt(in current dir). After reading the username (1st line) has to be stored in $user and the second line (password) has to be stored in $pass.
In case if this file is not found, I have to prompt the user to enter the username and password and store it in $user and $pass and create the file login.txt(in currect dir) and write these two lines into the file.
my $user;
my $pass;
my $login = "login.txt";
unless (-e $login) {
print "Entering first time execution mode....!\n";
sleep(2);
print "Enter username:\n";
$user = <STDIN>;
chomp($user);
print "Enter password:\n";
$pass = <STDIN>;
chomp($pass);
unless(open LINFO, '>'.$login) {
# Die with error message
# if we can't open it.
die "\nUnable to create $login\n";
}
print LINFO "$user\n";
print LINFO "$pass\n";
close LINFO;
}
if (-e $login) {
open (LINFO, '$login') or die "Cant open\n";
while( my $line = <LINFO>) {
print $line;
chomp($line);
if ($. == 1) { $user = $line; }
elsif ($. == 2) { $pass = $line; }
last if $. == 2;
}
close LINFO;
}
print $pass;
print $user;
When the file is doesn't exists everything works fine (even $user and $pass gets printed), but will get a error message as below :-
readline() on closed filehandle LINFO at loginfile.pl line 43.
If the file already exists then I get following error
readline() on closed filehandle LINFO at loginfile.pl line 43.
Use of uninitialized value $pass in print at loginfile.pl line 59.
Use of uninitialized value $user in print at loginfile.pl line 60.
Not sure whats the issue here
Why is it exiting if it doesn't find the login.txt file? Because of the die statement? Here's perldoc -f die:
die LIST
die raises an exception. Inside an eval the error message is stuffed
into $# and the eval is terminated with the undefined value. If the
exception is outside of all enclosing evals, then the uncaught
exception prints LIST to STDERR and exits with a non-zero value. If
you need to exit the process with a specific exit code, see exit.
While this is not terribly clear (to be precise, its horribly unclear for a newbie), you should probably know that die means that the program exits, execution stops there.
When using open, this is the recommended practice, since often any error in open calls should be considered fatal. You however ignore this open call and use another open on the same file handle later on in that block. The only reason I can see for
unless(open LINFO, $login) {
Would then be that you are trying to determine if the file exists, and you can read it. For this purpose, you can use the -X commands, e.g.
if (-e $login) # if file exists
if (-r $login) # effective uid/gid can read file
...
You have to enclose the conditions for if and elsif in parentheses. Also be careful where to use = and ==. This should work for you:
if ($. == 1) { $user = $line; }
elsif ($. == 2) { $pass = $line; }

How to write a correct name using combination of variable and string as a filehandler?

I want to make a tool to classify each line in input file to several files
but it seems have some problem in naming a filehandler so I can't go ahead , how do I solve?
here is my program
ARGV[0] is the input file
ARGV[1] is the number of classes
#!/usr/bin/perl
use POSIX;
use warnings;
# open input file
open(Raw,"<","./$ARGV[0]") or die "Can't open $ARGV[0] \n";
# create a directory class to store class files
system("mkdir","Class");
# create files for store class informations
for($i=1;$i<=$ARGV[1];$i++)
{
# it seems something wrong in here
open("Class$i",">","./Class/$i.class") or die "Can't create $i.class \n";
}
# read each line and random decide which class to store
while( eof(Raw) != 1)
{
$Line = readline(*Raw);
$Random_num = ceil(rand $ARGV[1]);
for($k=1;$k<=$ARGV[1];$k++)
{
if($Random_num == $k)
{
# Store to the file
print "Class$k" $Line;
last;
}
}
}
for($h=1;$h<=$ARGV[1];$h++)
{
close "Class$h";
}
close Raw;
thanks
Later I use the advice provided by Bill Ruppert
I put the name of filehandler into array , but it seems appear a syntax bug , but I can't correct it
I label the syntax bug with ######## A syntax error but it looks quite OK ########
here is my code
#!/usr/bin/perl
use POSIX;
use warnings;
use Data::Dumper;
# open input file
open(Raw,"<","./$ARGV[0]") or die "Can't open $ARGV[0] \n";
# create a directory class to store class files
system("mkdir","Class");
# put the name of hilehandler into array
for($i=0;$i<$ARGV[1];$i++)
{
push(#Name,("Class".$i));
}
# create files of classes
for($i=0;$i<=$#Name;$i++)
{
$I = ($i+1);
open($Name[$i],">","./Class/$I.class") or die "Can't create $I.class \n";
}
# read each line and random decide which class to store
while( eof(Raw) != 1)
{
$Line = readline(*Raw);
$Random_num = ceil(rand $ARGV[1]);
for($k=0;$k<=$#Name;$k++)
{
if($Random_num == ($k+1))
{
print $Name[$k] $Line; ######## A syntax error but it looks quite OK ########
last;
}
}
}
for($h=0;$h<=$#Name;$h++)
{
close $Name[$h];
}
close Raw;
thanks
To quote the Perl documentation on the print function:
If you're storing handles in an array or hash, or in general whenever you're using any expression more complex than a bareword handle or a plain, unsubscripted scalar variable to retrieve it, you will have to use a block returning the filehandle value instead, in which case the LIST may not be omitted:
print { $files[$i] } "stuff\n";
print { $OK ? STDOUT : STDERR } "stuff\n";
Thus, print $Name[$k] $Line; needs to be changed to print { $Name[$k] } $Line;.
How about this one:
#! /usr/bin/perl -w
use strict;
use POSIX;
my $input_file = shift;
my $file_count = shift;
my %hash;
open(INPUT, "<$input_file") || die "Can't open file $input_file";
while(my $line = <INPUT>) {
my $num = ceil(rand($file_count));
$hash{$num} .= $line
}
foreach my $i (1..$file_count) {
open(OUTPUT, ">$i.txt") || die "Can't open file $i.txt";
print OUTPUT $hash{$i};
close OUTPUT;
}
close INPUT;

Can a Perl subroutine return data but keep processing?

Is there any way to have a subroutine send data back while still processing? For instance (this example used simply to illustrate) - a subroutine reads a file. While it is reading through the file, if some condition is met, then "return" that line and keep processing. I know there are those that will answer - why would you want to do that? and why don't you just ...?, but I really would like to know if this is possible.
A common way to implement this type of functionality is with a callback function:
{
open my $log, '>', 'logfile' or die $!;
sub log_line {print $log #_}
}
sub process_file {
my ($filename, $callback) = #_;
open my $file, '<', $filename or die $!;
local $_;
while (<$file>) {
if (/some condition/) {
$callback->($_)
}
# whatever other processing you need ....
}
}
process_file 'myfile.txt', \&log_line;
or without even naming the callback:
process_file 'myfile.txt', sub {print STDERR #_};
Some languages offer this sort of feature using "generators" or "coroutines", but Perl does not. The generator page linked above has examples in Python, C#, and Ruby (among others).
The Coro module looks like it would be useful for this problem, though I have no idea how it works and no idea whether it does what it advertises.
The easiest way to do this in Perl is probably with an iterator-type solution. For example, here we have a subroutine which forms a closure over a filehandle:
open my $fh, '<', 'some_file.txt' or die $!;
my $iter = sub {
while( my $line = <$fh> ) {
return $line if $line =~ /foo/;
}
return;
}
The sub iterates over the lines until it finds one matching the pattern /foo/ and then returns it, or else returns nothing. (undef in scalar context.) Because the filehandle $fh is defined outsite the scope of the sub, it remains resident in memory between calls. Most importantly, its state, including the current seek position in the file, is retained. So each call to the subroutine resumes reading the file where it last left off.
To use the iterator:
while( defined( my $next_line = $iter->() ) ) {
# do something with each line here
}
If you really want do this you can by using threading. One option would be to fork a separate thread that reads the file and when it finds a certain line, place it in an array that is shared between threads. Then the other thread could take the lines, as they are found, and process them. Here is an example that reads a file, looks for an 'X' in a file's line, and does an action when it is found.
use strict;
use threads;
use threads::shared;
my #ary : shared;
my $thr = threads->create('file_reader');
while(1){
my ($value);
{
lock(#ary);
if ($#ary > -1){
$value = shift(#ary);
print "Found a line to process: $value\n";
}
else{
print "no more lines to process...\n";
}
}
sleep(1);
#process $value
}
sub file_reader{
#File input
open(INPUT, "<test.txt");
while(<INPUT>){
my($line) = $_;
chomp($line);
print "reading $line\n";
if ($line =~ /X/){
print "pushing $line\n";
lock(#ary);
push #ary, $line;
}
sleep(4)
}
close(INPUT);
}
Try this code as the test.txt file:
line 1
line 2X
line 3
line 4X
line 5
line 6
line 7X
line 8
line 9
line 10
line 11
line 12X
If your language supports closures, you may be able to do something like this:
By the way, the function would not keep processing the file, it would run just when you call it, so it may be not what you need.
(This is a javascript like pseudo-code)
function fileReader (filename) {
var file = open(filename);
return function () {
while (s = file.read()) {
if (condition) {
return line;
}
}
return null;
}
}
a = fileReader("myfile");
line1 = a();
line2 = a();
line3 = a();
What about a recursive sub? Re-opening existing filehandles do not reset the input line number, so it carries on from where it's left off.
Here is an example where the process_file subroutine prints out blank-line-separated "\n\n" paragraphs that contain foo.
sub process_file {
my ($fileHandle) = #_;
my $paragraph;
while ( defined(my $line = <$fileHandle>) and not eof(<$fileHandle>) ) {
$paragraph .= $line;
last unless length($line);
}
print $paragraph if $paragraph =~ /foo/;
goto &process_file unless eof($fileHandle);
# goto optimizes the tail recursion and prevents a stack overflow
# redo unless eof($fileHandle); would also work
}
open my $fileHandle, '<', 'file.txt';
process_file($fileHandle);