Opening file units recursively

J

John Ramsden

I have an application that reads a list of pathnames, and the
list can contain "include" lines each of which specifies the
path of a file to go off and process, just like recursive C
includes.

However, I am having a lot of trouble getting the file units
to open and close properly. The following is a short extract
of the relevant code, which works apart from the fact that
when wd_runcr() tries to close the 'include'd file the next
level file turns out to be closed at the same time.

Why do Perl file units have to be so _damned_ complicated
and obscure, when being passed between functions? Couldn't
the guy who first developed the language simply have used
ordinary variables for them?!

(I've also tried 'our' instead of 'local' No idea what
either does, but the examples I've seen seem to indicate
that for some reason something other than the usual 'my'
is required.)

use strict;

:::

sub wd_runcr
{
local ($filename, $fileunit) = @_;

no strict 'refs';

open $fileunit, "< $filename"
or wd_die "open ('$filename', 'r') failed:\n $!";

use strict 'refs';

while (<$fileunit>)
{
:::

if ($arg[0] eq '%include')
{
wd_runcr ($arg[1], ++$fileunit);

next;
}

:::
}

close $fileunit
or wd_die "close ('$filename') failed:\n $!";
}

#------------------------------------------------------------

:::

foreach my $subsys (@subsys)
{
my $file = "$subsys/$target_os_dir/wdruncr.txt";

wd_runcr ("$subsys/$target_os_dir/wdruncr.txt", 'A00');
}


Thanks in anticipation.


Cheers

John Ramsden ([email protected])
 
A

Anno Siegel

John Ramsden said:
I have an application that reads a list of pathnames, and the
list can contain "include" lines each of which specifies the
path of a file to go off and process, just like recursive C
includes.

However, I am having a lot of trouble getting the file units
to open and close properly. The following is a short extract
of the relevant code, which works apart from the fact that
when wd_runcr() tries to close the 'include'd file the next
level file turns out to be closed at the same time.

Why do Perl file units have to be so _damned_ complicated
and obscure, when being passed between functions? Couldn't
the guy who first developed the language simply have used
ordinary variables for them?!

Well, the guy apparently thought he could get away with filehandles
that were global symbols.

But times have changed and you *can* have filehandles in arbitrary
variables these days.
(I've also tried 'our' instead of 'local' No idea what
either does,

That's look-uppable. See "perldoc perlvar", "perldoc -f our",
and "perldoc -f local".
but the examples I've seen seem to indicate
that for some reason something other than the usual 'my'
is required.)

To the contrary, it's filehandles in lexical (my-) variables that makes
a smooth solution to the include problem possible.

[code snipped]

Here is an example that prints out the lines as they arrive. Any other
kind of processing could happen instead.

sub copy_including {
my $file = shift;
open my $fh, $file or die "Can't read $file: $!";
while ( <$fh> ) {
if ( my $incl = extract_incl( $_) ) {
copy_including( $incl);
} else {
print;
}
}
}

sub extract_incl {
local $_ = shift;
/#include\s*(.*)/;
$1;
}

The significant bit in this code is "my $fh" in the open statement.
This creates a new lexical $fh each time through, so opening $fh
doesn't close a file that has been opened in this variable before.
After return from a recursive call, the previous $fh is still around
and points to the right place.

Anno
 
T

Tad McClellan

John Ramsden said:
However, I am having a lot of trouble getting the file units
to open and close properly. The following is a short extract
of the relevant code, which works apart from the fact that
when wd_runcr() tries to close the 'include'd file the next
level file turns out to be closed at the same time.


Because $fileunit is a "package variable" (ie. a global variable).

The "inner" recursive subroutines are messing up the values of
variables used by the outer iterations.

Why do Perl file units


What is a "file unit"?

Do you mean "filehandle"?

Do you mean "file descriptor"?

Do you mean something else?

the guy who first developed the language


"the guy" has a name. It is Larry.

(I've also tried 'our' instead of 'local' No idea what
either does,


That is the cause of your problem, so you better keep at it until
you do understand them. These may help with that:

"Coping with Scoping":

http://perl.plover.com/FAQs/Namespaces.html

and

perldoc -q filehandle

How can I pass/return a {Function, FileHandle, Array,
Hash, Method, Regex}?
 
J

John Ramsden

I have an application that reads a list of pathnames, and the
list can contain "include" lines each of which specifies the
path of a file to go off and process, just like recursive C
includes.

Many thanks for the replies, which mentioned several interesting
points that I'll investigate.

On digging around the web shortly after posting I came across
http://www.stonehenge.com/merlyn/UnixReview/col19.html, which
explains the use of IO::File to do exactly the what I described.
(I would have mentioned this earlier, but Google delays several
hours before one's posts are visible.)

But the 'my $fh' solution is probably even simpler than using
IO::File.


Cheers

John Ramsden ([email protected])
 
A

Anno Siegel

John Ramsden said:
(e-mail address removed) (John Ramsden) wrote in message


Many thanks for the replies, which mentioned several interesting
points that I'll investigate.

On digging around the web shortly after posting I came across
http://www.stonehenge.com/merlyn/UnixReview/col19.html, which
explains the use of IO::File to do exactly the what I described.
(I would have mentioned this earlier, but Google delays several
hours before one's posts are visible.)

But the 'my $fh' solution is probably even simpler than using
IO::File.

Well, the column is more than five years old, quite possibly lexical
filehandles[*] weren't around then, or rather, weren't handled as smoothly.

The advantage of lexical filehandles is that the solution is now straight-
forward. Most programmers would write something similar in any recursive
language where filehandles can be held in variables.

It is much harder when you are stuck with global filehandles; recursion
doesn't look so attractive then. Once you have the recursive solution,
however, it can be retro-fit to package filehandles when you realize that
you can localize such a filehandle by localizing its typeglob. That
localizes everything under the name, including the filehandle. If you
now use

local *FH;
open FH, ...;

instead of

open my $fh, ...;

and read from "FH", recursion works as well with a package filehandle.
It should even work under Perl 4, though I didn't try. Code below, if
anyone has the opportunity and is so inclined...

[1] "Lexical filehandle" is short for "reference to a typeglob that contains
a filehandle, held in a lexical variable". They're so magic, they
glow in the dark.

Anno, who hasn't written Perl 4 for a long time

-------------------------------------------------------------
sub copy_including_4 {
no strict 'vars'; # for Perl 5 compatibility
local *FH;
open FH, $_[ 0] or die "Can't read $_[ 0]: $!";
while ( <FH> ) {
if ( /^#include\s*(.*)/ ) {
&copy_including_4( $1);
} else {
print;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top