http://www.perlmonks.org?node_id=11152753


in reply to How to Rename files in multiple directories with a Perl script using an array of hashes.

The starting point would be to narrow down where the problem is occurring. I suggest printing the contents of @path directly after setting it, to understand whether the problem is in glob or in rename. If you can, show us what the actual output is with the real filenames.

If the result from glob looks correct, try testing the same filenames on a local filesystem, and try testing a simple file on the NTFS mount - it is possible that File::Rename is seeing a Linux system and making some Unix-centrix assumptions that aren't valid for NTFS.

  • Comment on Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
  • Select or Download Code

Replies are listed 'Best First'.
Re^2: How to Rename files in multiple directories with a Perl script using an array of hashes.
by ObiPanda (Acolyte) on Jun 11, 2023 at 17:40 UTC

    I followed your suggestion of dumping the path array and discovered a slight issue with my code. One problem occurred when the directories (and perhaps files) had a space in the naming. I added a single parenthesis on each side and this seemed to fix the problem. The reason I didn't do this before was because I thought single quotes may render my variable a literal. The modification is  my @path = glob("'$Subscriptions_Path/$_->{Lib_Sub_Path}/*'");.

    glob might be my friend after all, I just wasn't treating it right. haha. The path array seems to be picking up the files in each of the directories now. So I guess it's the rename that isn't happy with what I'm passing it.

    Could rename have an issue with the file length? Here's why I ask.

    Going to your second suggestion, I slightly modified the working test code to account for spaces in the directory name. I'll provide the code and then the output, which appears to be perfectly correct here. (Bear in mind, this script is being run from my terminal in the VM but the script and the files and folders are all located on the NTFS file system which is shared with the VM.)

    #!/usr/bin/env perl use strict; use warnings; use autodie; use File::Find; use File::Rename; use Data::Dumper; # VARIABLES # Subscriptions Library & Archive Path variables for clarity my $Subscriptions_Path = "/mnt/hgfs"; # Subscription DATA sets my @Subscription = ( { Sub_Name => "T 1", Lib_Sub_Path => "VM-Share", Keep_Duration => 0, }, { Sub_Name => "T 2", Lib_Sub_Path => "VM-Share/xtr", Keep_Duration => 0, }, { Sub_Name => "T 3", Lib_Sub_Path => "VM-Share/xtr 2", Keep_Duration => 0, } ); for (@Subscription) { print "Beginning Subscription Service for $_->{Sub_Name} \n"; print "The absolute library path is: $Subscriptions_Path/$_->{Lib +_Sub_Path}\n"; my @path = glob("'$Subscriptions_Path/$_->{Lib_Sub_Path}/*'"); print Dumper(\@path); print "\n"; if (@path) { File::Rename::rename(\@path,{ _code => sub { s/_/ / }, verbose => 1, no_action => 1, } ); }; print "Completing Subscription Service for $_->{Sub_Name} \n\n"; }
      Using single quotes might solve some problems with spaces: but if the path contained a single quote, it would break again. As written in the documentation of glob, use bsd_glob from File::Glob if you don't want whitespace to be considered a separator.

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re^2: How to Rename files in multiple directories with a Perl script using an array of hashes.
by ObiPanda (Acolyte) on Jun 11, 2023 at 18:37 UTC

    Now that I have corrected the glob path array, only the FIRST _ character is being replaced, rather than every instance in the filename.

    I just added an additional _ to a file in my test area and this problem occurs with my test code too.

    Here's an example, names slightly modified.

    rename(/mnt/hgfs/Subscriptions/Dr Wendy Testaburger/Dr Wendy Testaburg +er 20230523 Post_Stan_world 1920x1080.mp4, /mnt/hgfs/Subscriptions/Dr + Wendy Testaburger/Dr Wendy Testaburger 20230523 Post Stan_world 1920 +x1080.mp4)

    For ease of reference, here is the relevant code:

    my @path = glob("'$Subscriptions_Path/$_->{Lib_Sub_Path}/*'"); if (@path != 0) { File::Rename::rename(\@path,{ _code => sub { s/_/ / }, verbose => 1, no_action => 1, } ); };

      Now that I have corrected the glob path array, only the FIRST _ character is being replaced, rather than every instance in the filename.

      Ah, that'll be because that's what you've asked for: to replace them all, you need to add the //g flag ("global") to the substitution:

          _code     => sub { s/_/ /g },

        Thank you.

      "... only the FIRST _ character is being replaced ..."

      I guessed that you just needed the /g modifier; however, as I've never used File::Rename previously, I ran a test to make sure.

      ken@titan ~/tmp/pm_11152739_file_rename $ ls -1 'test 4_5 6' test_1_2_3 'test_a b_c d_e f' $ perl -e ' use strict; use warnings; use File::Rename "rename"; my @files = ( "test 4_5 6", "test_1_2_3", "test_a b_c d_e f", ); rename \@files, { _code => sub { s/_/ /g; }, verbose => 1, no_action => 1, }; ' rename(test 4_5 6, test 4 5 6) rename(test_1_2_3, test 1 2 3) rename(test_a b_c d_e f, test a b c d e f) $ ls -1 'test 4_5 6' test_1_2_3 'test_a b_c d_e f' $ perl -e ' use strict; use warnings; use File::Rename "rename"; my @files = ( "test 4_5 6", "test_1_2_3", "test_a b_c d_e f", ); rename \@files, { _code => sub { s/_/ /g; }, verbose => 1, }; ' test 4_5 6 renamed as test 4 5 6 test_1_2_3 renamed as test 1 2 3 test_a b_c d_e f renamed as test a b c d e f $ ls -1 'test 1 2 3' 'test 4 5 6' 'test a b c d e f'

      [Aside: "if (@path != 0) ..." would more usually be written as just "if (@path) ...".]

      See also:

      — Ken