On a case-insensitive file system, such as NTFS or HFS+, given the name of a file, what is the most efficient way to determine the case-preserved version of the file name?
Consider on HFS+ (Mac OS X):
> perl -E 'say "yes" if -e "/TMP"'
yes
It says it exists, of course, but I have no idea how its case is preserved. What's the most efficient way to determine the actual case?
What I've tried so far:
glob with character classes: It doesn't work on Windows:
> perl -E "say for glob "C:\\Perl"
C:\Perl
> perl -E "say for glob "C:\\[Pp][Ee][Rr][Ll]"
Note the lack of output from that last command. :-(
opendir/readdir: Works, but seems rather inefficient to read an entire directory:
> perl -E "opendir my $dh, 'C:\\'; say for grep { lc $_ eq 'perl' } readdir $dh; close $dh"
Perl
Is it crazy to think that there ought to be some core operating system instructions or something to get at this information more efficiently?
The opendir/readdir/grep solution is the proper one. Via Twitter, Neil Bowers points to this quotation from perlport:
Don't count on filename globbing. Use
opendir,readdir, andclosedirinstead.
@miyagawa, also via Twitter, says that there is no system call for this, and if there was, it wouldn't be portable.
And given that @mob's answer and comments from David Golden suggest that glob would be more expensive than opendir, readdir, anyway, there just seems to be no other way around it.
So here's the function I wrote to find all the cases for a given basename in a directory:
sub _actual_filenames {
my $dir = shift;
my $fn = lc shift;
opendir my $dh, $dir or return;
return map { File::Spec->catdir($dir, $_) }
grep { lc $_ eq $fn } readdir $dh;
}
On Windows,
>perl -MWin32 -E"say Win32::GetLongPathName($ARGV[0])" "C:\PROGRAM FILES"
C:\Program Files
>perl -MWin32 -E"say Win32::GetLongPathName($ARGV[0])" C:\PROGRA~1
C:\Program Files
On unix, fcntl's F_GETPATH function will do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With