Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

in place editing a list of files

by rjsaulakh (Beadle)
on Jan 07, 2007 at 16:46 UTC ( [id://593405]=perlquestion: print w/replies, xml ) Need Help??

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

i have a file called edit where a list of file paths is given and in each of the file given in the path i need to match for multiple lines and delete them if they are there and save the file with the same file name . i have written a piece of code that works fine individually when $path is set to individual file but when i have file named edit contains multipe file path this script doesnt seem to do anything when it reaches the internal while loop . please let me know if i m doing some wrong

#!/usr/bin/perl #edit contains the complete path of the files to be edited my $path = "/home/rjs/heena/edit" ; #geting a file handler open(FL, "$path"); #getting all the file names in the @array so that i can process the fi +le one by one my @array = <FL>; while ( my $array = shift @array) { my $file = $array; @ARGV = ($file); # $INPUT_RECORD_SEPARATOR $/=""; # $INPLACE_EDIT $^I=".bak"; print "all fine \n "; while(<>) { # nothing seems to happen after this piece of code s/CONFIDENTIAL(.*?)own\s+risk/ /sm; print ; } }

a sample view of the file

/********************************************************************* +********/ /* + */ /* Copyright (c) 1996 - 2003 + */ /* Campaco Systems, Inc. + */ /* + */ /* All Rights Reserved + */ /* + */ /* CONFIDENTIAL and PROPRIETARY -- + */ /* No Dissemination or use without prior written permission + */ /* + */ /* Permission to use, copy, modify, distribute and sell this software +and */ /* its documentation for any purpose is hereby prohibited, unless gran +ted */ /* an explicit permission from Campaco Systems, and provided that the +above */ /* copyright notice appears in all copies and that both that copyright + */ /* notice and this permission notice appear in supporting documentatio +n. */ /* + */ /* Campaco Systems makes no representations about the suitability of t +his */ /* software for any purpose. + */ /* It is provided "as is" without express or implied warranty. Modific +ation */ /* of the code is allowed at the modifier's own risk.

Replies are listed 'Best First'.
Re: in place editing a list of files
by liverpole (Monsignor) on Jan 07, 2007 at 17:43 UTC
    Hi rjsaulakh,

    A lot of suggestions for you:

    • Use strict and warnings to make your life easier.
    • Indent your code to better show the control flow
    • Use the 3-argument form of open, and always check for success or failure.
    • Always close files as soon as you're done reading from them.
    • Use chomp (or chop) to remove the trailing newline, especially if the line represents a filename.
    • Use foreach to easily iterate through lists

    Putting them all together, you get the simplified:

    #!/usr/bin/perl # Always use these use strict; use warnings; # Edit contains the complete path of the files to be edited my $path = "/home/rjs/heena/edit" ; # Getting a file handler (and test for success/failure!) open(FL, "<", $path) or die "unable to open data file '$path' ($!)\n"; # Getting all the file names in the @array so that i can process the # file one by one chomp(my @array = <FL>); # Close the data file close(FL); # Process each file foreach my $file (@array) { next if ($file =~ /^\s*$/); # Skip blank lines from datafile open(FILE, "<", $file) or die "unable to open file '$file' ($!)\n" +; while(<FILE>) { s/CONFIDENTIAL(.*?)own\s+risk/ /sm; print; } close(FILE); }

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
      You seem to have missed the in-place edit part. And the part where the regex needs to affect multiple lines at once.

      liverpool thx a lot for ur piece of code but unfortunately the problem remains the same my files are not edited and they remain the same even after i run the script as modified by you . request you if u can guide me more on this i dont know where am i making the mistake

        Hi rjsaulakh,

        As ysth pointed out, my code was not doing the in-place editing part, nor the multi-line edit.  But those are quite easy to do.

        Here's a modified example that should work for you.

        Notice that the datafile can contain blank lines or commented-out lines, which are skipped (eg. # testfile).

        Notice, too, that the input file, once it's read and processed, is being written to, so it's REALLY an in-place edit.

        Warning:  Since it's doing in-place edits, be careful to have a backup copy of any files that you need to keep the original of!

        Here's the code:

        #!/usr/bin/perl + # Always use these use strict; use warnings; + # Edit contains the complete path of the files to be edited my $path = "/home/rjs/heena/edit" ; + # Getting a file handler (and test for success/failure!) open(FL, "<", $path) or die "unable to open data file '$path' ($!)\n"; + # Getting all the file names in the @array so that i can process the # file one by one chomp(my @array = <FL>); + # Close the data file close(FL); + # Process each file + foreach my $file (@array) { next if ($file =~ /^\s*(#|$)/); # Skip blank/commented lines process_file($file); # Process file } + + # Subroutines sub process_file { my ($file) = @_; + $/ = ""; # Read entire file as a single line my $fh; # Declare file handle (better than using globals) + # Read the file open($fh, "<", $file) or die "unable to read file '$file' ($!)\n"; $_ = <$fh>; close($fh); + # Apply the change s/CONFIDENTIAL(.*?)own\s+risk/ /sm; + # Write the file back TO THE SAME FILENAME open($fh, ">", $file) or die "unable to write file '$file' ($!)\n" +; print $fh $_; close $fh; }

        I made the file-processing into a subroutine for clarity, but you could do it within the loop if you wanted to, naturally.


        s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: in place editing a list of files
by ysth (Canon) on Jan 07, 2007 at 18:38 UTC
    Your code works fine for me, with the slight change of chomping $array at the beginning of the outer loop.

    But I'm not sure why you don't just put all the filenames in @ARGV:

    #!/usr/bin/perl use strict; use warnings; #edit contains the complete path of the files to be edited my $path = "./edit" ; #geting a file handler open(FL, "$path"); #getting all the file names in the @ARGV so that i can process them chomp(@ARGV = <FL>); # $INPUT_RECORD_SEPARATOR $/=""; # $INPLACE_EDIT $^I=".bak"; while(<>) { s/CONFIDENTIAL(.*?)own\s+risk/ /sm; print ; }
    It's possible you are having file permission problems, in which case, you are probably better off not relying on $^I but instead explicitly renaming each file and opening the old and new names, checking each operation for success and giving a meaningful error message on failure.
      ysth, you're correct, that it works fine if no errors occur.

      But as soon as the file "./edit" doesn't exist, or doesn't have the correct permissions, you'll get a cryptic:

      readline() on closed filehandle FL at C:\Documents and Settings\liverp +ole\edit.pl line 13.

      or something similar.  What's even worse is that, at least on my Windows box, the program doesn't terminate, forcing you to ^C to get out of it.

      That's why I was suggesting the error-checking when opening files.


      s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/

      the script run into endless loop once we enter the while loop and no substitution is being done

        I can only suggest you run it under the debugger, examining all variables as you go and seeing where things go differently than you expect. Alternatively, rewrite to not use the empty <> operator and explicitly do every open, rename, print, and close, checking for errors after each one.
Re: in place editing a list of files
by davidrw (Prior) on Jan 07, 2007 at 17:09 UTC
    doesn't directly address your code issues, but here's a different approach that should do what you want from a bash prompt:
    for f in `cat /home/rjs/heena/edit` ; do perl -i.bak -0777 -pe 's/CONFIDENTIAL(.*?)own\s+risk/ /s' $f done
    Update: shell-independent method: perl -i.bak -0777 -pe 's/CONFIDENTIAL(.*?)own\s+risk/ /s' `cat /home/rjs/heena/edit`

    If the 'edit' file contains a list of directories (i wasn't entirely sure from your post), then:
    for d in `cat /home/rjs/heena/edit` ; do perl -i.bak -0777 -pe 's/CONFIDENTIAL(.*?)own\s+risk/ /s' $d/* done
    See perlrun for info on the various switches (the -0777 causes a slurp).

      davidrw hats of to u sir

      shell-independent method: perl -i.bak -0777 -pe 's/CONFIDENTIAL(.*?)own\s+risk/ /s' `cat /home/rjs/heena/edit`

      you single line of code has done all the magic . just one more update i require from u , will this piece of code work fine when incorporated with another perl script

        depends what "incorporated" means ..

        does this need to be invoked from inside another script? if so obviously just can't copy/paste verbatim (could dump it in a system call, though maybe not ideal method) ..

        or does this just need to be called right before/after another perl script? if so, could just wrap the two calls in a shell script ...

        otherwise, write a sub that sets $^I, @ARGV, $/, and runs the loop that -n or -p does (see perlrun)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://593405]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-03-29 07:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found