Strange Interaction Between Image::ExifTool and File::Find::Rule

Started by shadwell61, December 14, 2021, 03:23:53 AM

Previous topic - Next topic

shadwell61

This is probably my fault.  I am new to ExifTool and new to Perl, so have probably done something silly, but it has me baffled.

I wrote a Perl script that searches for image files in a source tree given with a -s argument and optionally copies or moves them to a destination directory.  The destination root is given by a -d argument and the actual directory is built from the "DateTimeOriginal" extracted using Image::ExifTool.  The iteration across the source tree is done with a File::Find::Rule.

I used it to process the C: drive on my whole laptop without trouble, so I turned attention to an old USB memory stick as the source.  This fails completely and analysis showed that the root cause is in here:

my $success = $exift->ExtractInfo( $file );

This calls returns $success = 0 with the Error tag set to "Error opening file".

During troubleshooting I have reduced my script to its bare bones:

  • It ignores the result of the File::Find::Rule and calls ExtractInfo on the same hard-coded image file on the USB stick
  • The File::Find::Rule iterator is terminated after a single loop
What I find is that the call to ExtractInfo succeeds if I seed the iterator with the C: drive, but fails if I seed it with the D: drive, even though the results of the iterator are ignored.

Seeding with C: drive:

C:> perl ruletest2.pl -s C: -d Z:
Processing D:Personal/2019Christmas/IMG_20190928_130640.jpg
ExtractInfo succeeded
Died at ruletest2.pl line 49.
C:>

Seeding with the D: drive (USB stick):

C:>perl ruletest2.pl -s D: -d Z:
Processing D:Personal/2019Christmas/IMG_20190928_130640.jpg
ExtractInfo failed, status 0, error text: Error opening file
Died at ruletest2.pl line 49.
C:>


Here is the script in its cutdown form.

#!/usr/bin/env perl

use warnings;
use strict;

use Getopt::Long qw(:config no_ignore_case);
use File::Find::Rule;
use Image::ExifTool;

# Read program options
#
my $opts =
{
   src   => undef,
   dest  => undef,
};

GetOptions
(
   'source|s=s'    => \$opts->{src},               # Mandatory source directory
   'dest|d=s'      => \$opts->{dest},              # Mandatory destination directory
) or die;

my $match_extensions = "jpg||jpeg";

# Create a Find-Rule to match on file extensions in the match-extensions list.  Use
# ?i for case insensitivity
#
my $extRule = File::Find::Rule->new
                              ->file
                              ->name ( qr/(?i)\.($match_extensions)$/ )
                              ->start( "$opts->{src}" );

# Iterate through all the files matched by the rule
#

while ( defined ( my $imageFile2 = $extRule->match ) ) {

    # Hard code the image file (i.e. ignore those found by the iterator)
    #
    my $imageFile = "D:Personal/2019Christmas/IMG_20190928_130640.jpg";

    print "Processing " . $imageFile . "\n";

    # Read the EXIF file date
    #
    my %retVal = get_exif_date( $imageFile );

    die;  # Terminate loop after one iteration
}

# ------------------------------------
# get_exif_date
#
# This sub uses Image::ExifTool to pull DateTimeOriginal
#

sub get_exif_date
{
   my $file = $_[0];

   my $exift = new Image::ExifTool;

   my $success = $exift->ExtractInfo( $file );

   if ( $success != 1 )
   {
       print "ExtractInfo failed, status " . $success . ", error text: " . $exift->GetValue( "Error" ) . "\n";
   }
   else
   {
       print "ExtractInfo succeeded\n";
   }

   # Attempt to get the DateTimeOriginal attribute
   #
   my $date = $exift->GetValue( DateTimeOriginal => $file );
   my $source = "ExifDateTimeOriginal";

   my %retVal = ( 'FileDate', $date, 'DateSource', $source );
   return %retVal;
}


Any suggestions of what is going on are welcome.  How is the behaviour of the call to ExtractInfo influenced by the iterator when the results of the iterator are not being used ?

Thanks in advance for any help.

Phil Harvey

I'm sorry, but I don't think I can help much here.

1. I have never used File::Find::Rule

2. I haven't found a file iterator that works consistently on Windows with file/directory names containing special characters (especially emojis).

3. I have trouble testing things on Windows because my Windows machine is extremely slow.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

StarGeek

Normally, when I try and trouble shoot a problem, I add a ton of print statements to see what the values are as it goes through the program.  But that's mostly because I'm debugging while using the Windows executable.  A quick Google search shows that Perl has a debug mode that will allow you to step through the program line by line.  This page looked promising as a tutorial.  Bookmarking it myself for future use.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

shadwell61

Thanks Phil & StarGeek.

I tried the debug approach and tracked the processing all the way through to the end of Image::ExifTool::Open in ExifTool.pm.  The last line is the call to standard perl 'open':
    return open $fh, "$mode$file";

In both my test cases (seeded from C: and seeded from D:) I got to this point and examined the variables.

For the C: success case:

Image::ExifTool::Open(C:/Users/steve/Projects/strawberry-perl/perl/site/lib/Image/ExifTool.pm:3988):
3988:               $file = " $file\0";
  DB<2> n
Image::ExifTool::Open(C:/Users/steve/Projects/strawberry-perl/perl/site/lib/Image/ExifTool.pm:3991):
3991:       return open $fh, "$mode$file";
  DB<2> p $mode
<
  DB<3> p $file
D:Personal/2019Christmas/IMG_20190928_130640.jpg
  DB<4> p $fh
GLOB(0x3476ec8)
  DB<5> n
Image::ExifTool::ExtractInfo(C:/Users/steve/Projects/strawberry-perl/perl/site/lib/Image/ExifTool.pm:2399):
2399:                   $raf = new File::RandomAccess(\*EXIFTOOL_FILE);


For the D: fail case:

Image::ExifTool::Open(C:/Users/steve/Projects/strawberry-perl/perl/site/lib/Image/ExifTool.pm:3988):
3988:               $file = " $file\0";
  DB<2> n
Image::ExifTool::Open(C:/Users/steve/Projects/strawberry-perl/perl/site/lib/Image/ExifTool.pm:3991):
3991:       return open $fh, "$mode$file";
  DB<2> p $mode
<
  DB<3> p$file
D:Personal/2019Christmas/IMG_20190928_130640.jpg
  DB<4> p $fh
GLOB(0x348cf28)
  DB<5> n
Image::ExifTool::ExtractInfo(C:/Users/steve/Projects/strawberry-perl/perl/site/lib/Image/ExifTool.pm:2405):
2405:                   $self->Error('Error opening file');
  DB<5>


So the $mode and $file arguments to 'open' look identical (in both cases the filename is pre-pended with a space and has a trailing \0 added) but one 'open' succeeds and the other fails.

It was an interesting exercise but it hasn't got me any nearer to a solution  :(  Could there be an issue with the FileHandle ?