ellem has asked for the wisdom of the Perl Monks concerning the following question:

I want to append text to every .txt file
This is the directory listing:

Directory of S:\Shares\NETWOR~1\StandardLoads
12/18/2002 11:02a <DIR> . 12/18/2002 11:02a <DIR> .. 12/18/2002 11:06a 444 addtext.pl 12/18/2002 09:02a 0 Fiduciary_Group.txt 12/18/2002 09:02a 0 Nationwide_Group.txt 12/18/2002 09:02a 0 Relius_Group.txt 12/18/2002 09:02a 0 Sales_Group.txt 12/18/2002 11:02a 74 WordProcessing_Group.txt
This is the code:
#!C:\Perl\bin\perl.exe #Lou M 121702 @ GW use warnings ; use diagnostics ; use strict ; my $txt = "Windows 2000 SP3\nWindows Office XP\n" ; foreach my $file (<*.txt>) #For Each .txt file #in the directory #this script runs from { open ADDTO, ">> $file" or die "Can't open file: $!" ; } #Open all the .txt files #for appendding print ADDTO $txt; #Write the text print "Done\n\n" ; #Let the user know it is done
My script only seems to be working on one file, the one with 74 bytes, but (shamefully) I don't know why.
--
ellem@optonline.net
There's more than one way to do it, but only some of them actually work.

Replies are listed 'Best First'.
Re: foreach not working as I expected
by Tanalis (Curate) on Dec 18, 2002 at 16:55 UTC
    You need to move the print ADDTO line inside the foreach loop - all you're doing there is sequentially opening the files one after the other.

    It's not possible to open more than one file per filehandle - hence the handle is getting reassigned each time the loop iterates. This is why you get output in one file but not the others.

    It's also good practice to close each file when you're done appending, even though the file will be closed when the filehandle goes out of scope.

    A quick rewrite of your loop gives:

    foreach my $file (<*.txt>) { open ADDTO, $file or die "Couldn't open $file for append.\n"; print ADDTO $txt; close ADDTO; }

    This should give you what you expect.

    Hope that helps ..
    -- Foxcub

    Update: Added "or die" to the open.

      Well you can "open" several fields into one filehandle. Using some tie() magic. See IO::Tee on CPAN.

      But unless you really need to keep printing to several places at once i would not recomend this. If you only need to print a single thing into the files it's better to open+print+close them in the loop.

      Jenda

Re: foreach not working as I expected
by pfaut (Priest) on Dec 18, 2002 at 16:55 UTC

    Put the actions you want to do to the file inside the loop. And don't forget to close the file when done.

    #!C:\Perl\bin\perl.exe #Lou M 121702 @ GW use warnings ; use diagnostics ; use strict ; my $txt = "Windows 2000 SP3\nWindows Office XP\n" ; foreach my $file (<*.txt>) #For Each .txt file #in the directory #this script runs from { open ADDTO, ">> $file" or die "Can't open file: $!" ; print ADDTO $txt; #Write the text close ADDTO; } print "Done\n\n" ; #Let the user know it is done
    --- print map { my ($m)=1<<hex($_)&11?' ':''; $m.=substr('AHJPacehklnorstu',hex($_),1) } split //,'2fde0abe76c36c914586c';
      And don't forget to close the file when done.

      If you're going to check the status of the close, that's one thing (and it should be done more), but it's not really necessary if you're opening the same filehandle immediately. Here's a more paranoid version of the loop where I'd close:

      for my $file (<*.txt>) { open my $add, '>>', $file or die "Can't open file: $!\n"; print $add $txt; close $add or die "Can't close file: $!\n"; }

      Update: Oh yeah, in a long-running program that can be a problem. That's why I prefer to localize the glob or to use lexical filehandles. :)

        In this case, since the program is over when the loop completes, that's true. But what if there was more code beyond the end of the loop? If the handle wasn't explicitly closed inside the loop it would remain open.

        --- print map { my ($m)=1<<hex($_)&11?' ':''; $m.=substr('AHJPacehklnorstu',hex($_),1) } split //,'2fde0abe76c36c914586c';
        Why checking on the close :-)

        I always use IO::File and run the filehandle lexically. It gets close automaticly when it runs out of scope as you mention correctly in your update.


        ---------------------------
        Dr. Mark Ceulemans
        Senior Consultant
        IT Masters, Belgium

Re: foreach not working as I expected
by pg (Canon) on Dec 19, 2002 at 05:49 UTC
    Let's reuse as many existing tools as we can: (First save whatever you want to append in a file called tail.txt.)
    use File::Glob ':glob'; use strict; my @txt_files = bsd_glob("*.txt"); foreach my $txt_file (@txt_files) { system("copy $txt_file+tail.txt $txt_file"); }
      For one-time throw away tasks, maybe. This is expensive code though, particularly if there are tons and tons of files to be appended to. You are spawning shells for each iteration of the loop. These in turn fire off copy (which may or may not be a builtin to your shell) which in turn has to perform the same open, print, close logic your script would do anyway.

      Besides, it's not very perlish. {g}

      But, on an off topic for those who are saying "don't close your file, let the scope take care of it", please think some about maintainability. Someday a n00b will see your code and not understand how the scope issues work. They'll just think "oh, I guess you don't have to worry about closing things".
      {NULE}
      --
      http://www.nule.org

Re: foreach not working as I expected
by Anonymous Monk on Dec 19, 2002 at 16:24 UTC
    why not use file::find? pesudo code:
    use file::find find('*.txt',.,&doSomething); sub doSomething( my $fn = $_; open (YADA,$F "<"); )