在Perl脚本中删除Windows符号链接?

7
假设我创建了一些Windows符号链接,例如:
rd /s /q source withlink linkdir
mkdir source
mkdir withlink
echo blah > source/myfile
cd withlink
touch blah
mklink mylink ..\source\myfile
@REM mklink /d linkdir ..\source
cd ..

我可以在shell中使用以下命令删除包含符号链接的目录:

rd /s /q withlink

我有一个在perl脚本中需要完成的任务,目前我们使用cygwin的'rm -rf'。不幸的是,我们正在使用cygwin 1.5版本,rm和rm -rf在我想要使用的符号链接上无法正常工作(它们删除符号链接内容而不是符号链接)。

如果我尝试:

use File::Path qw( rmtree ) ;
rmtree( ['withlink'] ) ;

只要我没有任何目录符号链接(比如在上面创建链接序列中被注释掉的那个),这个方法就可以很好地工作,然后perl的rmtree会表现得像cygwin一样,最终导致原始目录中的内容被删除。
有人有任何替代perl递归目录删除方法的建议吗?我考虑过直接调用shell:
system("rd /s /q withlink") ;

但这需要我测试平台,并且为Windows和Unix编写不同的Perl代码。
编辑: 请注意,与Unix不同,unlink()无法删除目录符号链接,至少在perl v5.6.0中是如此,这是我们当前构建系统使用的版本。但是,rmdir()可以用于删除Windows目录符号链接。

抱歉,在我问问题时,我稍微编辑了标题,结果过早地不小心提交了一个不完整的问题。 - Peeter Joot
1个回答

4
在Windows中,您必须使用rmdir来删除目录符号链接和联接点,并且您必须使用简单的unlink来删除对文件的符号链接。原因是目录符号链接和联接点实际上是具有额外文件系统元数据(称为重分析点数据)的空目录。同样,对文件的符号链接是具有重分析点数据的空文件。当您阅读Microsoft NTFS文档时,这样的目录或文件被称为重分析点。重分析点类型由所谓的重分析点标记确定。有两个重分析点标记对用户可见,称为“链接”:

  1. IO_REPARSE_TAG_SYMLINK - 如果在目录上设置,则它是指向目录的符号链接;如果在文件上设置,则它是指向文件的符号链接。
  2. IO_REPARSE_TAG_MOUNT_POINT - 只能在目录上设置 - 它是所谓的“联接点”。它可以是指向另一个目录的“链接”,但它也可以是整个设备(卷)的“挂载点”。

总之:

  1. 您可以像处理目录一样删除对目录和联接点的符号链接(实际上,如果您检查此类对象的属性,则具有两个属性:FILE_ATTRIBUTE_DIRECTORY和FILE_ATTRIBUTE_REPARSE_POINT)。
  2. 您可以像处理文件一样删除对文件的符号链接。
  3. 在上述两种情况中,仅删除链接/联接(而不是目标)。
  4. 要查找文件夹是否为链接(所有类型):如果调用GetFileAttributesGetFileInformationByHandleFindNextFile,则属性包含FILE_ATTRIBUTE_REPARSE_POINT标志(例如:WIN32_FIND_DATA::dwFileAttributes)。如果它是指向目录的链接,它还包含标志FILE_ATTRIBUTE_DIRECTORY(请参见1.)。

希望这有所帮助。


这与我发现的一致,但感谢您提供的信息。您知道在Perl中查询这些文件系统标签的方法吗?还是必须编写调用FindFirstFile(和相关函数)的C代码? - Peeter Joot
是的,我有C++代码可以查询/设置/删除这些重分析标记,适用于Windows XP及更高版本。该代码太长且过于复杂,无法在此处发布,需要编写整篇文章来介绍它。它主要基于我的实验和阅读MSDN。从Vista开始,您可以使用CreateSymbolicLink函数创建符号链接或CreateHardLink(XP及更高版本)创建硬链接。我更新了我的答案 - 如何检测链接/联接 - 请参见上文。 - sirgeorge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接