Reputation: 311
I have a problem with detecting symbolic links under Windows 10, which supports them. First I tried this:
if(! -l $import_filename) {
print "$0: $import_filename is not a symlink";
}
That doesn't work. It gets executed when $import_filename is a symlink. Then I tried this:
use File::stat;
use Fcntl;
my $statbuf = lstat($import_filename);
if(!($statbuf->mode & S_ISLNK)) {
print "$0: $import_filename is not a symlink";
}
And it seems to be a different way to say the same thing. As expected, is there any blessed way to do this under Windows versions with symlink/junction support? If there isn't, a command line tool is also an acceptable answer.
Upvotes: 1
Views: 1148
Reputation: 66891
There seem to be not much Perl support for working with symlinks on Windows (if any). Neither of related builtins are implemented, according to perlport page for symlink and for readlink.
Most importantly for your direct question, lstat isn't implemented either so one can't have a filetest for symlink. The perlport for -X says that
-g
,-k
,-l
,-u
,-A
are not particularly meaningful.
I haven't found anything on CPAN, other than using the Windows API.
Then you can go to the Windows command line, and look for <SYMLINK>
in dir
output
# Build $basename and $path from $import_filename if needed
if ( not grep { /<SYMLINK>.*$basename/ } qx(dir $path) ) {
say "$0: $import_filename is not a symlink";
}
where $basename
need be used since dir
doesn't show the path. The components of a filename with full path can be obtained for example with the core module File::Spec
use File::Spec;
my ($vol, $path, $basename) = File::Spec->splitpath($import_filename);
If the filename has no path then $vol
and $path
are empty strings, which is OK for dir
as it needs no argument for the current directory. If $import_filename
by design refers to the current directory (has no path) then use it in the regex, with qx(dir)
(no argument).
The dir
output shows the target name as well, what can come in handy.
Upvotes: 0
Reputation: 385916
Given
>mklink file_symlink file
symbolic link created for file_symlink <<===>> file
>mklink /d dir_symlink dir
symbolic link created for dir_symlink <<===>> dir
>mklink /h file_hardlink file
Hardlink created for file_hardlink <<===>> file
>mklink /j dir_hardlink dir
Junction created for dir_hardlink <<===>> dir
>dir
...
2018-05-09 12:59 AM <JUNCTION> dir_hardlink [C:\...\dir]
2018-05-09 12:58 AM <SYMLINKD> dir_symlink [dir]
2018-05-09 12:56 AM 6 file_hardlink
2018-05-09 12:58 AM <SYMLINK> file_symlink [file]
...
You can use the following to detect file_symlink
, dir_symlink
and dir_hardlink
(but not file_hardlink
) as a link:
use Win32API::File qw( GetFileAttributes FILE_ATTRIBUTE_REPARSE_POINT );
my $is_link = GetFileAttributes($qfn) & FILE_ATTRIBUTE_REPARSE_POINT;
I don't know how to distinguish between hard links and symlinks (though differentiating between files and dirs can be done using & FILE_ATTRIBUTE_DIRECTORY
).
Upvotes: 3