WSL2 发行版 shell 无法启动从外部复制的文件

WSL2 distro shell can't launch a file copied from outside

简述情况

我无法在 WSL2 发行版中启动可执行文件(二进制或脚本),如果它不是在该发行版中创建的话

我可以启动在发行版 shell 中创建的脚本和二进制文件(不以任何方式使用 /mnt/c 或 /mnt/d)

但我无法启动在外部创建并从 Windows 内部复制的任何内容(使用 /mnt/c 或 /mnt/d)

我可以在文件系统中看到复制的文件,可以“cat”它们,可以用“which”查找它们,但是我无法通过在命令行中输入路径来启动它们

我对这一切的疑问

详细情况

我有 Windows 10 个 WSL2 和两个发行版

在 Ubuntu 我有一个“Hello, World!”用 C 编写的项目

它在 Ubuntu 中编译,然后在 Ubuntu 中编译 运行 就好了

但是,当我将它从 Ubuntu 复制到 Windows

cp hello /mnt/d/

然后去Alpine里面从Windows

复制进去
cp /mnt/d/hello .

然后我无法在 Alpine 中启动它

这是 Ubuntu 中 file hello 命令的输出,带有一些额外的格式(以防万一)

$ file hello
hello:
    ELF 64-bit LSB shared object, 
    x86-64, 
    version 1 (SYSV), 
    dynamically linked, 
    interpreter /lib64/ld-linux-x86-64.so.2, 
    BuildID[sha1]=021352ab7bf244e340c3c42ce34225b74baa6618, 
    for GNU/Linux 3.2.0, 
    not stripped

这是我在 Alpine 所拥有的

$ cp /mnt/d/hello .
$ ls -l
-rwxr-xr-x    1 pavel    pavel        16760 Apr 19 19:07 hello
$ ./hello
-ash: ./hello: not found

现在与从 Windows

复制的脚本相同

从Windows

复制Alpine里面的脚本
$ cp /mnt/d/hello.sh .

检查内容

$ cat hello.sh
#!/bin/ash
echo Hello!

设置执行权限以防万一

$ chmod agu+x hello.sh

正在尝试 运行 它

$ ./hello.sh
-ash: ./hello.sh: not found

但是,我可以通过显式调用 ash 工具并将脚本路径作为参数传递来启动 hello.sh

$ ash ./hello.sh
Hello!

同时,在 Alpine 运行s 中创建的脚本只需输入它的命令行路径

$ cat << EOF > hello-local.sh
> #!/bin/ash
> echo Local hello!
> EOF
$ chmod agu+x hello-local.sh
$ ./hello-local.sh
Local hello!

此外,我无法通过使用 cp

复制一个不会 运行 的文件
cp hello.sh hello2.sh

或者用 cat

复制它
cat hello.sh > hello3.sh
cmod agu+x hello3.sh

为什么我需要从外面复制东西

一切始于我想探索 Docker for Windows 如何使用 Linux 命名空间来分隔容器

Docker for Windows 使用的发行版称为 docker-desktop

docker-desktop 发行版既没有我实验所需的实用程序,也没有获取这些实用程序的包管理器

所以我试着从外面复制它们

但现在 Docker Windows 研究并不是唯一的问题

我想了解这种正在发生的魔法同样糟糕

公平地说,这里确实有三个独立的问题,但不一定是您在 post:

中列出的问题

第二个问题 -- 为什么您复制到 Alpine 的脚本会失败?

正如@MarkPlotnick 在评论中所提到的(并且您已确认),这是由于脚本具有 DOS/Windows 行结尾 (CRLF)。一般来说,尽量避免使用 Windows 工具创建或编辑 Linux 文本文件,除非您确定他们正在使用 Linux line-endings.

第二个问题 -- 为什么在 Ubuntu 上编译并将二进制文件复制到 Alpine 时,您的 C 程序会失败?

还有@MarkPlotnick在评论中提到的,这是因为Ubuntu默认使用glibc作为标准库实现,而Alpine使用musl。请参阅按“相关性”排序的列表中的 a number of questions here for more information. The first one 实际上是一个很好的开始。

主要问题 -- 如何探索 docker-desktop 发行版

实际上,您的主要目标似乎是如何访问 docker-desktop 发行版中的某些工具以了解更多信息。

我本来想说“不要”(有更多解释),但事实是我认为这是一种潜在的良好学习体验。在某种程度上,我已经做到了,那么我有什么资格说它“太危险”或建议不要这样做呢? ;-)

不过,我会给出合理的警告 -- docker-desktop 发行版并非 打算 由用户 运行 发行。 Docker Desktop 将链接和套接字“注入”到您的其他 WSL2 发行版(您可以在 Docker Desktop 中 enable/disable per-distro),以便其工具、进程等适用于您的所有 WSL2(和 PowerShell/CMD)实例。

我个人会尽量避免对 docker-desktop 发行版本身进行任何 更改。当 Docker Desktop 提取新的 rootfs 时,它们可能会被覆盖。

但是,我们仍然可以通过 另一个发行版访问它们来访问我们需要的工具,而不是将它们复制 docker-desktop.

首先,请注意——我想您可能已经发现,docker-desktop 也是 musl-basesd。因此,您需要使用来自另一个基于 musl 的发行版(例如 Alpine)的工具。

这可以通过 运行 在您的 Alpine 实例中(作为 root)执行一次以下行来轻松完成:

echo "/ /mnt/wsl/instances/Alpine none defaults,bind,X-mount.mkdir 0 0" >> /etc/fstab

这会将 Alpine 实例的挂载添加到 tmpfs /mnt/wsl 挂载中。您可以查看 my Super User answer here 了解更多详细信息。

一旦 wsl --terminate Alpine 并重新启动它,您就可以从任何其他 WSL2 发行版访问 Alpine 文件。

作为一个有用的(为了您的意图)示例,在 Alpine 中安装 util-linux 包以访问 lsns 命令。

然后,在 docker-desktop 发行版中(我假设您已经知道使用 wsl -u root -d docker-desktop 进行访问,但我将在此处包含该命令以供其他未来的读者使用),以列出命名空间:

/mnt/host/wsl/instances/Alpine/usr/bin/lsns

docker-desktop 实例自动挂载到与默认目录略有不同的目录(参见 cat /etc/wsl.conf),因此您需要将路径调整为 /mnt/host/wsl 而不是 /mnt/wsl

但是有了它,您可以 运行 直接在 docker-desktop 中 运行 所有(大部分?)您的 Alpine 二进制文件,而无需直接修改它。如果您的主目录中有一个脚本,您希望在 docker-desktop 中 运行,例如:

/mnt/host/wsl/instances/Alpine/home/users/<yourusername>/hello.sh

请注意,如果您的二进制文件需要 Alpine 上的 dynamically-linked 库,我假设您需要相应地调整 LD_LIBRARY_PATH,尽管我还没有测试过。例如:

LD_LIBRARY_PATH=/mnt/host/wsl/instances/Alpine/usr/lib /mnt/host/wsl/intances/Alpine/usr/bin/<whatever>