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
, andclosedir
instead.
@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