为啥 Files.isHidden(Path) 为 Windows 上的目录返回 false?

Posted

技术标签:

【中文标题】为啥 Files.isHidden(Path) 为 Windows 上的目录返回 false?【英文标题】:Why does Files.isHidden(Path) return false for directories on Windows?为什么 Files.isHidden(Path) 为 Windows 上的目录返回 false? 【发布时间】:2019-05-16 10:24:42 【问题描述】:

来自Files.isHidden(Path) 的文档(强调我的):

判断文件是否被视为隐藏。隐藏的确切定义取决于平台或提供商。例如,在 UNIX 上,如果文件名以句点字符 ('.') 开头,则认为文件被隐藏。 在 Windows 上,如果文件不是目录并且设置了 DOS 隐藏属性,则认为文件是隐藏的。

根据实现的不同,此方法可能需要访问文件系统以确定文件是否被视为隐藏。

由此我可以理解什么预期的行为。然而,为什么这是预期的行为?

我想知道的原因是Files.isHiddenDosFileAttributes.isHidden 和 Windows 的文件资源管理器之间的行为差​​异。例如,我可以进入 文件资源管理器 并将一个目录设置为隐藏,它将不再显示(除非我将其配置为显示隐藏项)。如果我测试是否使用 Java 隐藏了所述目录,则 Files.isHidden 返回 falseDosFileAttributes.isHidden 返回 true。您可以使用以下代码对此进行测试:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.DosFileAttributes;

public class Main 

  public static void main(String[] args) throws Exception   
    final var directory = Path.of(args[0]).toAbsolutePath().normalize();
    final var store     = Files.getFileStore(directory);
    final var dosAttrs  = Files.readAttributes(directory, DosFileAttributes.class);

    System.out.println("Directory     : " + directory);
    System.out.println("FileStore     : " + store.name() + " [" + store.type() + "]");
    System.out.println("Hidden (Files): " + Files.isHidden(directory));
    System.out.println("Hidden (Dos)  : " + dosAttrs.isHidden());
  



注意:我使用的是 Windows 10 和 OpenJDK 11.0.1。我的文件系统是 NTFS


运行这个:

java Main.java C:\path\to\hidden\directory

我明白了:

Directory     : C:\path\to\hidden\directory
FileStore     : OS [NTFS]
Hidden (Files): false
Hidden (Dos)  : true

注意:此行为似乎是 WindowsFileSystemProvider 的一部分。方法Files.isHidden(Path) 只是将调用转发给参数的FileSystem 的提供者。实现基本上是:

DosFileAttributes attrs = ...; // get attributes
return !attrs.isDirectory() && attrs.isHidden();

我发现这个(non)-issue (JDK-8170334) 有一条评论说:

我认为我们这里没有错误,因为 hidden 属性对目录没有意义。

然而,Windows 上的核心软件 File Explorer 的行为就像 hidden 属性在目录上 not 毫无意义。再说一遍,为什么 Windows 上的 Java 实现会考虑 Path 是否指向目录?还是 Java 是正确的,而 文件资源管理器 正在做非标准的事情?

我倾向于认为 File Explorer 是正确的,因为 CMD(通过 dir)和 PowerShell(通过 Get-ChildItem ) 也不会列出隐藏目录;除非指定了适当的选项,否则不会。

【问题讨论】:

我不是 Java 开发人员,也不打算深入研究 JDK 设计问题,但从表面上看,这在我看来就像有人混淆了 hidden 和 readonly 属性。 Windows shell (Explorer) 会忽略在目录上设置 readonly 属性,因为它没有用。它所做的只是防止删除一个空目录。相比之下,在 Linux/BSD 中的目录上设置不可变属性(通过 chattr / chflags,通常作为超级用户)可以防止以任何方式修改目录。 在我的盒子 "C:\" 上使用 dosAttrs.isHidden() 时要小心,返回 True @Stig 此处相同(Java 12.0.1,Windows 10)。但是,PowerShell 还说 "C:\" 是隐藏的,所以这可能是预期的行为。 我同意这不是一个错误,但它会让大多数人感到惊讶。 【参考方案1】:

我检查了documentation 以获取 Microsoft 为 Windows 平台提供的文件属性。它说如果设置了属性FILE_ATTRIBUTE_HIDDEN = 2 (0x2)

文件或目录被隐藏。它不包含在普通目录列表中。

正如我在 sun.nio.fs.WindowsConstants 类中看到的那样,DosFileAttributes.isHidden() 方法使用了相同的值定义 - public static final int FILE_ATTRIBUTE_HIDDEN = 0x00000002; 在我的理解中应该与 Windows 可用的属性一一对应,所以在目录的一般hidden 标志应该以与常规文件相同的方式工作。 关于操作系统/文件系统集成,这种行为似乎是不正确的。

【讨论】:

纠正Files.isHidden() 错误将破坏现有的Windows Java 应用程序,这些应用程序尽职尽责地遵循其对Windows 系统上的隐藏目录的错误定义,而无需进一步检查DosFileAttributes.isHidden()。这可能是 Java API 开发人员不想让它成为问题的原因,因为我们仍然可以使用 DosFileAttributes.isHidden() 来弥补正确的行为。 我最终提交了一个错误报告:JDK-8215467。我们会看看情况如何,但@ecle 可能是正确的,并且由于他们的评论中解释的原因,它不会被修复;如果它没有像其他两个错误报告那样被关闭为“不是问题”。 原来这是一个错误。报告称它已在 Java 13 中修复。

以上是关于为啥 Files.isHidden(Path) 为 Windows 上的目录返回 false?的主要内容,如果未能解决你的问题,请参考以下文章

即使路径不存在,为啥 Path(...).exists 为真? [复制]

为啥 Powershell 环境 PATH 与系统环境 PATH 不同?

为啥 sudo 会更改 PATH?

为啥我要将 python 添加到 PATH

UTC 中的 Python os.path.getmtime 为啥

为啥在字符串连接上使用 os.path.join?