为什么某些 cygwin 符号链接在 cmd.exe 会话中不可见

why are some cygwin symlinks not visible from a cmd.exe session

总结:

在最近(2020年9月)版本的64位cygwin中,/bin//lib目录下的symlinks有令人费解的特征,似乎不同于任何其他各种记录的 symlinks。问题是:

  1. 为什么它们对 CMD.EXE 隐藏,但在其他地方可见,不像 其他符号link 类型

  2. 是否可以创建具有相同功能的符号link?如果可以,怎么做?

TL;DR 请参阅@matzeri 的回答:SYSTEM 属性在 /bin/awk 上设置,使其对 CMD.EXE 会话不可见。

/bin/lib 下面的 symlinks 似乎是此处描述的两种“默认 cygwin symlinks”之一:

The default symlinks created by Cygwin are either special reparse points shared with WSL on Windows 10, or plain files containing a magic cookie followed by the path to which the link points. The reparse point is used on NTFS, the plain file on almost any other filesystem. see symbolic links

但是,/bin 以下的所有 symlink 都设置了 SYSTEM 属性,而 /lib 以下的大部分 symlink 都没有。我不知道我是如何在 NTFS 上用 magic cookie 创建一个 symlink。

下面是问题的长版

cygwin下有多种symlink。其他问题解释了它们的历史,描述了它们的差异,并展示了如何创建它们等。例如:

check the difference of symlink type in cygwin

下面是 windows 上关于 symlink 的一些历史和问题的讨论:

Symlinks on the mounted Windows directories are not compatible with native

如前所述,有快捷方式、连接点和 symlinks,所有这些都可以从 CMD.EXE 会话中看到,如图所示。

但是,在我的64位cygwin安装下,还有一种我找不到任何解释。其中一些出现在 /bin/lib 以及其他地方。将它们与其他 3 个区分开来的主要特征是它们在 CMD.EXE 会话中不可见。

举个例子: 如果我尝试从 /bin 目录通过 CMD.EXE 列出 'awk',该文件似乎不存在:

$ cd /bin

$ cmd.exe /c dir awk

 Volume in drive C is opt
 Volume Serial Number is D0C8-EA58

 Directory of C:\cygwin64\bin

File Not Found

这是 ls 的样子:

$ ls -l awk
lrwxrwxrwx 1 philwalk None 8 May 14 12:26 awk -> gawk.exe

但根据我的经验,大多数 symlink 都可以从 CMD.EXE 会话中看到。让我们创建一个 symlink 用于测试:

$ CYGWIN=winsymlinks:nativestrict # make sure we don't create an old-style symlink
$ ln -s `which ls.exe` ls-link
$ cmd.exe /c dir ls-link
 Volume in drive C is opt
 Volume Serial Number is D0C8-EA58

 Directory of C:\opt\ue

2020-09-03  13:12    <SYMLINK>      ls-link [C:\cygwin64\bin\ls.exe]
               1 File(s)              0 bytes
               0 Dir(s)  295,290,556,416 bytes free

这说明CMD.EXE可以看到这种类型的symlink。

似乎所有 symlink 都可以像这样取消引用:

$ cygpath -w `which awk`
C:\cygwin64\bin\gawk.exe

那么基于 windows 的程序可以解引用它们吗?它能看到它们吗?因为它们对 CMD.EXE 不可见,我猜想它们对任何其他 windows(即非 cygwin)程序都不可见。

这是一个 scala 脚本,deref.sc,将测试这些问题:

#!/usr/bin/env scala

import java.nio.file._

for( posixPath <- args ){
  val cyg = cygpath(posixPath)
  val windows = Paths.get(posixPath)
  val exists = Files.exists(windows)
  printf("file path [%s]\n",posixPath )
  printf("cygpath   [%s]\n",cyg)
  printf("windows   [%s]\n",windows)
  if( exists ){
    printf("exists  [%s] # visible to jvm-based programs\n",exists)
  } else {
    printf("exists  [%s] # NOT visible to jvm-based programs\n",exists)
  }
  val realpath = if( exists ){
    if( Files.isSymbolicLink(windows) ){
      Files.readSymbolicLink(windows)
    } else {
      windows.toRealPath()
    }
  }
  printf("real windows [%s]\n",realpath)
  printf("\n")
}
def cygpath(posixPath:String) = {
  import scala.sys.process._
  Seq("cygpath.exe","-m",posixPath).lazyLines_!.mkString("")
}

这是应用于 /bin/awk 和 ./ls-link 的 deref.sc 的输出。 (我们必须通过windows-可见版本的/bin/awk)

$ deref.sc ls-link
cygpath   [ls-link]
windows   [.\ls-link]
exists  [true] # visible to jvm-based programs
real windows [C:\cygwin64\bin\ls.exe]
$ cd /bin

$ deref.sc awk
file path [awk]
cygpath   [C:/cygwin64/bin/gawk.exe]
windows   [awk]
exists  [true] # visible to jvm-based programs
real windows [C:\cygwin64\bin\awk]

所以这表明,虽然有些windows程序(比如CMD.EXE)看不到c:/cygwin64/bin/awk,但是其他的(比如基于jvm的程序)可以!

我最初的问题是基于 windows 程序无法看到这些新符号 link 的错误假设,由于这是不正确的,所以剩下的问题是:

这些新符号是什么link,它们是如何创建的?它们真的不同,还是这可能是权限的副作用? 运行 CMD.EXE 作为管理员似乎没有什么区别。

我验证了 c:/cygwin64/bin/awk 也可以从 WSL 会话中看到,尽管这并不奇怪,因为 jvm 可以看到它们。

更新:虽然 /bin/awk 对我们的程序可见,但 java.nio.file.Files.isSymbolicLink() 并未将其视为符号 link。它的内容由字符串 "!<symlink>gawk.exe" 组成,因此它似乎是 symlinks 的早期 cygwin 实现,在 windows 提供本机 symlinks 之前。

除了在 CMD.EXE.

中不可见外,它没什么特别的

Cygwin 比 NTFS 对符号链接的支持要老得多。因此,Cygwin 有自己的符号链接实现,它本质上只是一个包含文本 !<symlink>gawk.exe 并以 ASCII NUL 字符终止的文本文件。

链接不可见的原因是它们的文件属性

根据 DOS/Windows 设计,

S = System 在 CMD 中不可见,
来自 CMD,对不起德语,我们有:

$ cmd
Microsoft Windows [Version 10.0.19041.450]
(c) 2020 Microsoft Corporation. Alle Rechte vorbehalten.

D:\cygwin64\bin>attrib zipinfo
   S                 D:\cygwin64\bin\zipinfo

D:\cygwin64\bin>dir zipinfo
 Datenträger in Laufwerk D: ist DATA
 Volumeseriennummer: D603-FB6E

 Verzeichnis von D:\cygwin64\bin

Datei nicht gefunden

D:\cygwin64\bin>dir /A:S zipinfo
 Datenträger in Laufwerk D: ist DATA
 Volumeseriennummer: D603-FB6E

 Verzeichnis von D:\cygwin64\bin

19.06.2018  22:17                16 zipinfo
               1 Datei(en),             16 Bytes
               0 Verzeichnis(se), 542.542.495.744 Bytes frei

我使用 GoodSync 将安装 Cygwin 的卷同步到 NAS。这是由更改触发的,应该在后台运行。一段时间以来(我不能准确地说什么时候),Cygwin 安装程序似乎默认创建 Windows 本机 links(在许多情况下,不是全部)。将系统变量 CYGWIN 设置为 winsymlinks:lnk 似乎没有帮助。在系统范围 bash 配置文件中进行设置也无法解决问题。

但是,由于无法从 Windows 应用程序正确解析这些 link(link /etc/alternatives/2to3 指向 /usr/bin/2to3-3.8),请阅读它们来自 GoodSync 的文件将失败 - 除了无法备份它们之外。 GoodSync 中符号 links 上的选项“忽略”没有达到预期的效果(也许它只尊重真正的符号 links,而不是重新分析点 - 我已经报告过那个错误)。

所以,我的解决方案是注意此类错误,然后 运行 此脚本包含涉及的路径列表,以将 link 修复回旧的 Cygwin 风格的 .lnk 文件.

#! /bin/bash
CYGWIN=winsymlinks:lnk; export CYGWIN

for file in $* ; do
    if [ -L "$file" ] ; then
    t="`readlink $file`"
    echo "$file: symbolic link -> $t"
    if [ -L "$file.lnk" ] ; then
        echo "   already ok"
    else
        rm "$file"
        ln -s "$t" "$file"
        echo "   fixed"
    fi
    else
    echo "$file: not a link"
    fi
done

exit 0

对于初始扫描,我 运行 对整个 Cygwin 安装进行了 find / -type l -print 扫描,过滤掉了存在同名 .lnk 文件的路径,并修复了其余路径。

在 WSL 中,存在相同的问题 - 除了没有简单的解决方案。符号 links 是 Windows 原生 links,如果它们指向 Unix 风格的路径则无法读取。除了仅求助于相对目标路径之外,没有其他表示法。这些(可能)也有机会被 Windows.

正确解释

从具有此类 link 的文件系统创建基于文件的 1:1 备份仍然是一个挑战。