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

Perl Monks, I need some assistance better understanding an issue. I have a script that compares two files and produces an output of differences from the second file . The issue I am having is adding an additional code to produce the differences from the first file it does not output anything or produce any errors. Below is my script.

#!/usr/local/bin/perl use strict; use warnings; my $a_file; my $b_file; # quit unless we have the correct number of command-line args my $num_args = $#ARGV + 1; if ($num_args != 5) { print "\nUsage: name.pl new_filename orig_filename domain new_tool +s orig_tools\n"; exit; } # args my $new_filename=$ARGV[0]; my $orig_filename=$ARGV[1]; my $domain=$ARGV[2]; my $new_tools=$ARGV[3]; my $orig_tools=$ARGV[4]; my $psconfig = "psconfig.sh"; if ($new_filename eq $psconfig) { $a_file = "/directory/$new_filename" ; $b_file = "/directory/$orig_filename" ; } open my $a_fh, '<', $a_file or die "file error $a_file: $!"; open my $b_fh, '<', $b_file or die "file error $b_file: $!"; my %second_file; print "\n--------------------------------------------\n"; print "Output from $new_tools $domain $new_filename\n"; print "--------------------------------------------\n"; @second_file{map { unpack 'A*', $_ } <$b_fh>} = (); while (<$a_fh>) { print unless exists $second_file{unpack 'A*', $_}; } my %first_file; print "\n--------------------------------------------\n"; print "Output from $orig_tools $domain $orig_filename\n"; print "--------------------------------------------\n"; @first_file{map { unpack 'A*', $_ } <$a_fh>} = (); while (<$b_fh>) { print unless exists $first_file{unpack 'A*', $_}; }

Replies are listed 'Best First'.
Re: output using map
by Fletch (Bishop) on Mar 06, 2020 at 20:27 UTC

    When you use a filehandle in a list context (e.g. your line @second_file{map { unpack 'A*', $_ } <$b_fh>} = ();) it reads the entire contents splitting it into lines (modulo $/ of course; see perlvar). The next time you use that same filehandle, since everything's been read from the file it's just going to return undef because it's at the end of the file. You need to either close and re-open it, or use seek to set the file pointer back at the beginning of the file (at which point reading from it will again return the lines).

    @second_file{map { unpack 'A*', $_ } <$b_fh>} = (); seek( $b_fh, 0, 0 ); ## Reset $b_fh back to the beginning

    That aside, if you're on a *NIX-y system you might be interested in the diff or cmp commands instead.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: output using map
by Marshall (Canon) on Mar 06, 2020 at 23:12 UTC
    I am not sure what you are trying to accomplish with the unpack A* code? I show a modified version of your code below. It does sound like "diff" or other system utility would be just as good at this? I am on Windows, but my editor has a "diff" button for 2 different editor windows (a common feature of a program editor).

    I didn't test this and I'm sure there is bound to be some mistake somewhere, but be that as it may, consider this:

    #!/usr/local/bin/perl use strict; use warnings; my $a_file; # you need an initial value for these names! my $b_file; # in case the if statement doesn't work # quit unless we have the correct number of command-line args if (@ARGV != 5) { print "\nUsage: $0 new_filename orig_filename domain new_tools ori +g_tools\n"; exit(-1); } # args my ( $new_filename, $orig_filename, $domain, $new_tools, $orig_tools ) = @ARGV; my $psconfig = "psconfig.sh"; if ($new_filename eq $psconfig) { $a_file = "/directory/$new_filename" ; $b_file = "/directory/$orig_filename" ; } open my $a_fh, '<', $a_file or die "file error $a_file: $!"; open my $b_fh, '<', $b_file or die "file error $b_file: $!"; my %first_file = map { chomp; $_ => 1}<$a_fh>; my %second_file = map { chomp; $_ => 1}<$b_fh>; print "\n--------------------------------------------\n"; print "Output from $new_tools $domain $new_filename\n"; print "--------------------------------------------\n"; foreach (keys %first_file) { print "$_\n" unless exists $second_file{$_}; } print "\n--------------------------------------------\n"; print "Output from $orig_tools $domain $orig_filename\n"; print "--------------------------------------------\n"; foreach (keys %second_file) { print "$_\n" unless exists $first_file{$_}; }
      I am not sure what you are trying to accomplish with the unpack A* code?

      I think you're right that unpack is intended to chomp the newline (and maybe all whitespace?) from the end of each line.

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my $s = qq{abc defg hi \t \n}; my $t = unpack 'A*', $s; dd $t; " "abc defg hi"
      (It's also unclear why chomp-ing would be necessary since lines are print-ed with a newline appended; no newline is appended in the OPed code, but maybe that potential problem was obscured by the fact that nothing was ever printed!)

      Note that the keys (lines) of each file will be printed in random order.


      Give a man a fish:  <%-{-{-{-<

        I think you are correct about the intent of unpack. However this is a bad way to do it, if that is the intent. There might also be some issue related to UTF-8 filenames? But again this is not the "right way".

        I chomped the input lines and added back in a platform specific line ending in the print because in general, I don't store line endings in data structures because I strive for multi-platform code.

        Yes, foreach (keys %first_file) {} could be: foreach (sort keys %first_file) {}

      Thank You. I guess I was overthinking what I wanted to do which was to get the diff between both files output. I modified my code according to the changes you had posted and it is working as I expected.

Re: output using map
by jo37 (Curate) on Mar 07, 2020 at 08:51 UTC

    The loop

    while (<$a_fh>) { print unless exists $second_file{unpack 'A*', $_}; }

    eats up the whole file. Maybe your intention was like:

    while (<$file_a>) { unless (exists $h{unpack 'A*', $_}) { print; } else { last; } }

    And then there is of course the other issue with using <$file_x> in list context as already described by Fletch and the questionable use of unpack.

    -jo