$filename='Z:\repo\bin\tools\scripts\shared\users\script.pl';
$key =~ /\Q$filename\E/i;
#matches (notice different starts and ends)
Z:\repo\bin\tools\scripts\shared\users\script.pl
Z:\repo\bin\tools\scripts\shared\users\script.plm
Z:\repo\bin\tools\scripts\shared\users\script.plx
Z:\repo\bin\tools\scripts\shared\users\script.pl\foobar
\\?\Z:\repo\bin\tools\scripts\shared\users\script.plm
# and does NOT match
Z:/repo/bin/tools/scripts/shared/users/script.pl
The extra matches come because /\Q$filename\E can match anywhere within a string, not just beginning to end. To match the whole string you need to start with a caret and end with a dollar sign: /^\Q$filename\E$/. However if you are matching the whole string and using quotemeta (\Q...\E) you might as well just do lc $key $eq lc $filename. lc makes both sides of the equality lower case, making case irrelevant just as the i flag on the regex does.
The missed matches come from the MS-Windows operating systems having several different ways of expressing the exact same path. Making your regex case insensitive may not be enough to get all the matches you want.
I forget exactly when MS started recognizing '/' in paths, but certainly XP onward, you can use either '/' or '\' to separate path segments. One solution to that is to preprocess your paths by substituting '\' for '/', e.g. grep { lc $_ eq lc $filename } map { s/\//\\/g; $_ } @paths or grep { my $x=$_;$x =~ s/\//\\/g; lc $x eq lc $filename } @paths if you want to preserve the original paths in your output array.
Update: added alternative that preserves original paths. |