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

Happy New Year Monks!
My question and code posted below. I would like to delete the SAME line in multiple files. The below code works fine for 2 files however I have an additional 3 files or more the data could show up in but not always. Instead of listing each file is there away to combine EVERYTHING into one block of code? Thanks for the help!

my $process_file = $rrconfig::locodata; my $process_file2 = $rrconfig::availlocodata; my @data; my @data2; { open(my $data_file_handle, '<', "$process_file") or die("Can't open file: $process_file"); open(my $data_file_handle2, '<', "$process_file2") or die("Can't open file: $process_file2"); @data = <$data_file_handle>; @data2 = <$data_file_handle2>; close($data_file_handle); close($data_file_handle2); close $process_file; close $process_file2; } chomp(@data); chomp(@data2); open(my $out_file_handle,'>', $process_file) or die("Can't open file: $process_file"); open(my $out_file_handle2,'>', $process_file2) or die("Can't open file: $process_file2"); foreach my $line_from_file ( @data ) { my @field = split( /\:/, $line_from_file ); next if( ($field[1] eq $delroad) && ($field[2] == $delnumber) ); print $out_file_handle $line_from_file, "\n"; } foreach my $line_from_file2 ( @data2 ) { my @field2 = split( /\:/, $line_from_file2 ); next if( ($field2[1] eq $delroad) && ($field2[2] == $delnumber) ); print $out_file_handle2 $line_from_file2, "\n"; } close $out_file_handle; close $out_file_handle2; close $process_file; . close $process_file2;

Replies are listed 'Best First'.
Re: Delete Lines Across Multiple Files
by haukex (Archbishop) on Dec 31, 2020 at 17:16 UTC

    It looks to me like the pieces of code relating to $process_file and $process_file2 are basically identical, which means you can simply do the same thing in a loop. Note you've got a stray . on the second-to-last line of your code, and the multiple close $process_file statements don't make sense, as those variables hold the file names, not handles.

    for my $process_file ($rrconfig::locodata, $rrconfig::availlocodata) { open(my $data_file_handle, '<', $process_file) or die("$process_fi +le: $!"); my @data = <$data_file_handle>; close($data_file_handle); chomp(@data); open(my $out_file_handle, '>', $process_file) or die("$process_fil +e: $!"); foreach my $line_from_file ( @data ) { my @field = split( /\:/, $line_from_file ); next if( ($field[1] eq $delroad) && ($field[2] == $delnumber) +); print $out_file_handle $line_from_file, "\n"; } close $out_file_handle; }

    Although I feel like I've been plugging it pretty often lately, note this code can be simplified using inplace editing, using either $^I (an example, but note that -i has a few caveats like in some cases only issuing a warning when something goes wrong, instead of dying), or my module File::Replace. This has the advantage of not having to load the entire file into memory, like the code above does.

    use File::Replace 'replace3'; for my $process_file ($rrconfig::locodata, $rrconfig::availlocodata) { my ($infh, $outfh, $repl) = replace3($process_file); while ( my $line = <$infh> ) { chomp($line); my @field = split( /\:/, $line ); next if ($field[1] eq $delroad) && ($field[2] == $delnumber); print $outfh $line, "\n"; } $repl->finish; }

      @haukex: thanks for the advice I will try that. If I load 3 files and the data is only in 2 files will my 3rd file become blank? or will it be left alone? Thanks!

        If I load 3 files and the data is only in 2 files will my 3rd file become blank? or will it be left alone?

        The code you wrote, which I just put in the loop, just does this: split the line on colons, check if the second and third fields match certain values and if yes skip that line via next, otherwise write the line back out via print. In other words, it will delete the lines that match the condition, all the other lines remain unchanged. This is true for any file the code processes. This is the advantage of the loop, you know every file will be treated exactly the same - unless of course there's a fatal error on one of the files, in which case the rest won't be processed, but there are ways to handle that too.

        Minor edits.

Re: Delete Lines Across Multiple Files
by tybalt89 (Monsignor) on Jan 01, 2021 at 15:37 UTC

    Alternate with "missing file" protection.

    #!/usr/bin/perl use strict; use warnings; use Path::Tiny; my $delroad = 'Some Road Name'; my $delnumber = 123; for my $file ( qw( file1 file2 ... file3 ) ) # your filenames here { eval { path($file)->edit_lines( sub { my @field = split /:|\n/; $field[1] eq $delroad && $field[2] == $delnumber and $_ = ''; } ); 1; } or print $@; }
Re: Delete Lines Across Multiple Files
by Marshall (Canon) on Jan 03, 2021 at 03:35 UTC
    Here is another solution for you.
    This could be made more general purpose, but...
    use strict; use warnings; use Inline::Files; my @files = qw (FOO BAR FOOBAR MOREFOO); omit_lines(\@files, 5, 42); sub omit_lines { my ($files, @nums) = @_; foreach my $fileName (@$files) { print "*** $fileName ***\n"; while (<$fileName>) { next unless /^\S/; #skip blank lines my (@digits) = $_ =~ /(\d+)/g; next if ( ($digits[0] == $nums[0]) and ($digits[1] == $nums[1]) ); print; } } } =prints: The lines with 5, 42 are gone.. *** FOO *** abc 1 32 *** BAR *** asdf 2 12 *** FOOBAR *** xyz 3 43 *** MOREFOO *** abc 1 32 xyz 3 43 =cut __FOO__ abc 1 32 __BAR__ asdf 5 42 asdf 2 12 __FOOBAR__ xyz 3 43 asdf 5 42 __MOREFOO__ abc 1 32 asdf 5 42 xyz 3 43 asdf 5 42
Re: Delete Lines Across Multiple Files
by jwkrahn (Abbot) on Dec 31, 2020 at 22:50 UTC

    This may work (UNTESTED):

    my @files = ( $rrconfig::locodata, $rrconfig::availlocodata, # add other file names here ); { local @ARGV = @files; local $^I = '.back'; # change '.back' to '' if your system supports it while ( <> ) { next if /^[^:]*:$delroad:$delnumber:/; print; } }