Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
by hv (Prior) on Jun 10, 2023 at 22:03 UTC
|
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.
| [reply] [d/l] [select] |
|
|
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";
}
| [reply] [d/l] [select] |
|
|
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]
| [reply] [d/l] [select] |
|
|
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,
}
);
};
| [reply] [d/l] [select] |
|
|
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 },
| [reply] [d/l] [select] |
|
|
|
|
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:
| [reply] [d/l] [select] |
Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
by kcott (Archbishop) on Jun 11, 2023 at 00:00 UTC
|
G'day ObiPanda,
Welcome to the Monastery.
"Glob is not my friend."
Without any examples of your "real subscription folder files", I can't give specific advice;
however, here are some suggestions:
-
Build up $glob_expression separately and then just call glob($glob_expression).
As an example, see my "Re: Set intersection problem" post from a couple of weeks ago, where I had to do something similar.
-
The glob() documentation
has examples of dealing with filenames containing spaces (which can be considered as a special character).
You may be able to do something similar with whatever special characters your filenames contain.
-
The File::Glob module may have useful information;
in particular, GLOB_LIMIT seems relevant.
-
Show us some representative samples of your real data so that we can provide better answers. :-)
| [reply] [d/l] [select] |
|
|
Thank you. The Perldocs File::Glob documentation helped me with a mistake I was making. I may have been confusing Perl with another language, but I had thought that single quotes don't work with variables. I am not a programmer.
It looks as though my issue may not be glob anymore.
| [reply] |
Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
by haukex (Archbishop) on Jun 14, 2023 at 10:29 UTC
|
| [reply] [d/l] [select] |
Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
by Anonymous Monk on Jun 11, 2023 at 18:32 UTC
|
Rather than wrestle with the glob() function's semantics around spaces, you might want to consider using bsd_glob() instead. This is exported by File::Glob, which is a core module. The bsd_glob() function takes all the meta-characters (?, *, [...], and {...,...}) but treats a space as a normal character.
| [reply] [d/l] [select] |
Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
by jwkrahn (Abbot) on Jun 11, 2023 at 23:44 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
# 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 my $curr_sub ( @Subscription ) {
print "Beginning Subscription Service for $curr_sub->{Sub_Name}\n"
+;
chdir "$Subscriptions_Path/$curr_sub->{Lib_Sub_Path}" or do {
print "Could not find directory: $Subscriptions_Path/$curr_sub
+->{Lib_Sub_Path}\n";
next;
};
opendir my $DH, '.' or die "Cannot open directory '$Subscriptions_
+Path/$curr_sub->{Lib_Sub_Path}' because: $!";
while ( my $file = readdir $DH ) {
next unless -f $file;
my $new = $file;
$new =~ s/_/ /g;
rename $file, $new or do {
print "Could not rename '$file' because: $!";
next;
};
}
print "Completing Subscription Service for $curr_sub->{Sub_Name}\n
+\n";
}
Naked blocks are fun!
-- Randal L. Schwartz, Perl hacker
| [reply] [d/l] |
Re: How to Rename files in multiple directories with a Perl script using an array of hashes.
by harangzsolt33 (Deacon) on Jun 11, 2023 at 00:01 UTC
|
I, too, use Microsoft Windows. I run my perl scripts with TinyPerl, which is not great but it is sufficient for my needs. Anyways, I am not a perl expert. I'm very far from it! But I know that accessing files with Unicode characters in the name is a lot of hassle. So, I wrote a JavaScript program which runs in Windows (just save it as GetRidOfUnicode.js and double-click it) and it scans the current directory and all sub-directories and renames all files and directories which have Unicode characters or extended ASCII characters in the name and changes them to standard ASCII plain text characters. It's a quick and dirty script which I wrote in haste. But it works.
I have used Hungarian letters in my file names, and it was a bad habit. Anyway, this script changes many Hungarian letters and some others to plain text ASCII, and the rest of the Unicode characters are changed to spaces.
After you run this, all files will become easily accessible from perl:
http://www.wzsn.net/js/RenameUnicode.txt
| [reply] |