shemyaza has asked for the wisdom of the Perl Monks concerning the following question:
I am writing my first perl program at the moment (see below).
What I want to do is insert the contents of general.txt into
the start of all of the files listed in a file called
files.txt.
The code below works for the first file listed in files.txt
but I can't figure out how to get it to loop through all
of the files in files.txt. I thought of counting the number
of lines in files.txt and using a 'for' loop, but that's a
bit inelegant.
Can you help?
S.
#!/usr/local/bin/perl
open (TEXT,"general.txt") || die "Could not open file: $! \n";
$addthis=join("", <TEXT>);
close (TEXT);
open (FILES,"files.txt") || die "Could not open file: $! \n";
# Start to loop here??? or after the next group of statements?
$line=<FILES>;
chomp $line;
open (DATA,"$line") || die "Could not open file: $! \n";
$tothis=join("", <DATA>);
close (DATA);
$newrecord=$addthis.$tothis;
open (DATA,">$line") || die "Could not write to: $! \n";
print "$line\n";
print DATA $newrecord;
close (DATA);
Re: How to Put a Loop in my 1st Program
by sifukurt (Hermit) on Nov 21, 2001 at 19:42 UTC
|
I tried to follow your methodology as closely as possible. There are easier and/or more efficient ways of doing what you're trying to do, but personally, I'd worry about style after you're a little more comfortable with the language as a whole. Not to discount style, mind you, but as with a spoken language, you don't worry about writing a sonnet or a haiku until you've learned how to write a sentence or paragraph.
#!/usr/local/bin/perl
use strict;
use vars qw( $addthis $tothis $newrecord );
open (TEXT, "general.txt") || die "Couldn't open file: $|\n";
$addthis = <TEXT>;
close (TEXT);
undef $/;
open (FILES,"files.txt") || die "Could not open file: $! \n";
while ( <FILES> ) {
open ( DATA, $_ ) || die "Could not open file: $|\n";
$tothis = <DATA>;
close (DATA);
$newrecord = $addthis . $tothis;
open ( DATA, "> $_" ) || die "Could not write to file: $|\n";
print $_, "\n";
print DATA $newrecord;
close (DATA);
}
close (FILES);
Since you're treating all of the files as individual strings, you can do the undef $/; business. $/ is the input record separator, and since we aren't limiting the scope (i.e., putting undef $/; inside a subroutine, for example), it is a global change and will apply to all subsequently read files. And in this case, I think a 'while' loop is one of the easier ways to setup the looping.
I don't want this to sound like a shameless plug, but I wrote the module File::Butler, and it was specifically designed to handle this sort of thing. And since File::Butler does all of the grunt work for you, you don't need to worry about most of it. Using File::Butler, you could re-write the above as this:
#!/usr/local/bin/perl
use strict;
use File::Butler;
use vars qw( @newstuff @files );
Butler( "general.txt", "read", \@newstuff );
Butler( "files.txt", "read", \@files );
foreach my $file ( @files ) {
Butler( $file, "prepend", \@newstuff );
}
I hope this helps. Welcome to the Perl Fraternity, btw. We'll teach you the secret handshake later. :-)
Update: Pursuant to discussion with tilly on the use of our vs. use vars, I've modified the above code to use vars rather than our as I had done originally. I wasn't aware that our differed so much in theory and practice.
Update2: Eeeeeek! Hofmator is absolutely correct. I had undef $/; in the wrong place. Thanks for catching that, Hofmator. I moved it down a few lines, and it will work correctly now. I'm now performing undef $/; after we've read in "files.txt".
___________________
Kurt
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
open(FH, 'filename') or die $!;
my $text = do {local $/; <FH>};
close FH;
For a complete treatmeant of many possibilities and discussions on how to slurp a file read the very informative Slurp a file.
-- Hofmator | [reply] [Watch: Dir/Any] [d/l] |
Re: How to Put a Loop in my 1st Program
by MZSanford (Curate) on Nov 21, 2001 at 19:26 UTC
|
Rather than give a working copy, here is something to point you in the right direction :
while (my $line = <FILES>) {
# open the file ($line) as DATA
# manipulate the file
# close(DATA);
}
close(FILES);
i had a memory leak once, and it ruined my favorite shirt. | [reply] [Watch: Dir/Any] [d/l] |
Re: How to Put a Loop in my 1st Program
by frankus (Priest) on Nov 21, 2001 at 19:58 UTC
|
Congratulations on a fine choice of programming language ;o)
A few really good habits to start with are:
#!/usr/bin/perl -w
use strict;
This takes away the niceness of not having to declare variables, but makes spotting bugs waaaay faster ;)
$addthis=join("", <TEXT>);
Whoa!! Nice use of join, it makes sense now I think about it. I'd suggest using ' instead of " as " interpolates stuff, and if you don't need it use ' instead ;)
open (FILES,"files.txt") || die "Could not open file: $! \n";
I prefer: open FOO,'foo.txt' or die "$! foo.txt\n";
Maybe use a while loop or a for loop, as for loops can traverse arrays too.
for (<FILES>){
chomp; # each item of the array is placed in $_ for the scope of t
+he for
unless (open (D,$_)){ # no need to wrap the variable in quotes
print STDERR "$!:$_\n"; # In case the failure to read one file
+ isn't critical
next; # Skips all the other commands in the loop and goes to t
+he next iteration.
}
my $newrecord=$addthis.join('',<D>);
close(D);
open (D,">$_") || die "Could not write to: $! \n";
print "$_\n";
print D $newrecord;
close (D)
}
Watch yourself here, DATA is a reserved word IIRC like:
#!/usr/bin/perl -w
use strict;
$someperl=<DATA>;
__DATA__
Some data
Now $someperl should contain "Some data".
--
Brother Frankus.
¤ | [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to Put a Loop in my 1st Program
by c-era (Curate) on Nov 21, 2001 at 19:28 UTC
|
You want to use a while loop,
#!/usr/local/bin/perl
open (TEXT,"general.txt") || die "Could not open file: $! \n";
$addthis=join("", <TEXT>);
close (TEXT);
open (FILES,"files.txt") || die "Could not open file: $! \n";
# Start to loop here??? or after the next group of statements?
while ($line=<FILES>){
chomp $line;
open (DATA,"$line") || die "Could not open file: $! \n";
$tothis=join("", <DATA>);
close (DATA);
$newrecord=$addthis.$tothis;
open (DATA,">$line") || die "Could not write to: $! \n";
print "$line\n";
print DATA $newrecord;
close (DATA);
}
| [reply] [Watch: Dir/Any] [d/l] |
Re: How to Put a Loop in my 1st Program
by shemyaza (Novice) on Nov 21, 2001 at 22:35 UTC
|
Thanks for your help! I will try and avoid the temptation of studying
the 'full' answers until I've tried to sort it out myself with your helpful tips.
I'm afraid I can't take credit for my use of 'join'! I found
that on the perlmonks site yesterday using google. It's such a great site
I just had to register, so here I am! :-)
S. | [reply] [Watch: Dir/Any] |
|
Studying full answers isn't necessarily bad, especially not when you learn from them.
Did you read the semi-official Perl literature yet? "Programming Perl" (aka the Camel book) might be too hard, but "Learning Perl" should be easier (I haven't read it). Both are in their third editions already, and more information can be found at http://perl.com/ or http://oreilly.com/ or in the better book stores. (No, they don't pay me for this, and if I don't say it, someone else will. These books are recommended to every Perl newbie here :-))
Or try the Tutorials section in the Monastery. It has tutorials on basic stuff like scalars and loops, but also on more advanced topics.
But I think that reading a book is usually a smart idea, as these books are more coherent than loose tutorials, and paper reads differently.
Good luck, and don't forget to have fun in the process :-)
| [reply] [Watch: Dir/Any] |
|
|