为什么进程替换会产生一个名为 /dev/fd/63 的文件,它是一个管道?

Ram*_*esh 49 process pipe open-files process-substitution

我试图在这个特定示例的上下文中理解命名管道。

我输入<(ls -l)我的终端并得到输出,bash: /dev/fd/63: Permission denied.

如果我输入cat <(ls -l),我可以看到目录内容。如果我更换catecho,我觉得我得到的终端名称(或者是什么呢?)。

echo <(ls -l)给出输出为/dev/fd/63

此外,这个示例输出我不清楚。

ls -l <(echo "Whatever")
lr-x------ 1 root root 64 Sep 17 13:18 /dev/fd/63 -> pipe:[48078752]
Run Code Online (Sandbox Code Playgroud)

但是,如果我给出,ls -l <()它会列出目录内容。

在命名管道的情况下会发生什么?

cra*_*150 51

当你这样做时<(some_command),你的 shell 执行括号内的命令并用文件描述符替换整个内容,该文件描述符连接到命令的标准输出。/dev/fd/63包含 ls 调用输出的管道也是如此。

当你这样做时,<(ls -l)你会得到一个Permission denied错误,因为整行都被管道替换了,实际上试图/dev/fd/63作为一个命令调用,它是不可执行的。

在您的第二个示例中,cat <(ls -l)变为cat /dev/fd/63. 当 cat 从作为参数给出的文件中读取时,您将获得内容。echo另一方面,只是“按原样”输出其参数。

您拥有的最后一种情况<()只是被空无所取代,因为没有命令。但这在 shell 之间并不一致,在 zsh 中你仍然会得到一个管道(虽然是空的)。

摘要<(command)让您使用命令的输出,您通常需要一个文件。

编辑:正如Gilles指出的那样,这不是命名管道,而是匿名管道。主要区别在于,它只存在,只要进程正在运行,而命名管道(例如创建mkfifo)将保持不附加进程。

  • 这里没有命名管道。`/dev/fd/63` 是一个匿名管道。 (9认同)
  • `mkfifo` 只创建命名管道,没有任何内容。所以你需要自己写(例如`mkfifo mypipe; ls &gt; mypipe`)。是的,对管道的写入将阻塞,直到某个进程从管道读取。 (5认同)
  • @rv 它仍然是一个匿名管道。有一个文件名指向这个匿名管道的事实并不能使它成为命名管道:命名管道是不同的,它存在于文件系统的某处,具有权限和所有权等。`/dev/fd 的条目` 可以引用任何文件描述符,甚至是匿名管道和套接字、网络套接字、共享内存段等。 (3认同)
  • 不过,为什么是_63_? (3认同)
  • @K3---rnc 管道将具有的确切路径不是固定的,并且可能因外壳甚至外壳版本而异。所以 63 只是一些非保留的文件描述符,bash 似乎通过确定性方法选择。 (3认同)
  • @crater2150,@Gilles /dev/fd/63 确实是一个命名管道。使用“file &lt;(ls)”之类的内容进行检查。shell 确实创建了一个匿名管道,但文件描述符在“/dev/fd”中反映为命名管道。如果它是一个匿名管道,它将没有名称,并且无法通过传递“/dev/fd/63”的命令打开。 (2认同)