有人问如何将两个命令的输出作为文件传递给另一个命令,他们得到了下面的答案。
( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )
我需要打开这个。
假设我有一个文本文件some_file
,我希望将其作为输入传递给main_command
. main_command
将两个文件作为输入。如果我想与命令的输出一起使用main_command
,some_file
一种cmd2
方法是
( cmd2 | ( main_command some_file /dev/fd/4 ) 4<&0 )
- 这其中“最深”的部分(即一切达到高潮的地方)是
main_command some_file /dev/fd/4
. 这只是将文件some_file
和/dev/fd/4
作为参数传递给main_command
. - 该
4<&0
部分表示stdin
将指向文件描述符4
。 cmd2 |
将 的输出cmd2
与后面的任何内容的输入连接起来。- 我真的不知道括号的功能是什么。它们只是为了解析目的而存在还是更多?
我的问题是:
- 如何解压问题开头的命令?
- 括号有什么作用?
- 我对更简单命令的解释是否正确?
编辑:我应该说如果我的逻辑是正确的,那么没有必要回答 1。
这是一个相当复杂的命令。我已经在最后直接回答了你的问题,但在那之前所有这一切都是在解压命令本身。我试图做到全面,所以在某些地方可能会有比你需要的更多的细节。
括号创建一个子shell :
表示从当前的 shell 派生一个新的 shell,在其中执行
x y z
(然后返回到当前 shell)。子外壳继承了当前外壳的所有内容,但它是一个单独的进程:这意味着它可以将输入通过管道传输到其中,并且可以在内部进行不影响父外壳的自己的环境更改。每个打开的文件都有一个与之关联的数字“文件描述符” 。在此上下文中的“文件”包括任何类型的输入或输出流,包括真实文件、套接字和标准 I/O 流。这些数字是句柄,可以直接与C
read
函数一起使用,以识别您正在谈论的流,以及操作系统提供的相应系统调用以及所有其他 IO 函数。4<&0
执行重定向,将标准输入文件描述符 (0) 克隆为文件描述符 4。这意味着FD 0 被复制到 4,而不是相反。在这种情况下,它正在修改重定向之前的子 shell 的打开文件。目前,这只是为输入流创建另一个“名称”。但关键部分是这两个名称此后相互独立:FD 4 将始终引用同一个流,即使 FD 0 更改为引用其他内容并且两者不同。/dev/fd/4
是程序访问自己打开的文件描述符的一种(非标准)方式。在 Linux 上,它是一个指向 的符号链接/proc/self/fd
,它具体化了当前进程的文件描述符表。一个程序open("/dev/fd/4", O_RDONLY)
可以获得一个文件句柄,该句柄引用该程序在 FD 4(例如它4
自己)上的流。就程序而言,这只是一个可以像任何其他文件一样打开、关闭和读取的常规文件。因为打开的文件描述符是由子main_command
进程继承的,所以文件描述符 4 与它所在的子 shell 相同,因此/dev/fd/4
也可以在那里工作。cmd2 | x
运行cmd2
,并将其标准输出连接到标准输入(或 FD 0)x
。在您的命令中,x
是子shell 表达式。我们的总体指挥
然后有三个主要部分:
cmd2
并将其输出通过管道传输到( main_command /dev/fd/4 ) 4<&0
.4
标识的流命名。0
( main_command /dev/fd/4 )
main_command
,/dev/fd/4
它将(可能)作为文件打开并从中读取,得到cmd2
.最后的效果是
main_command
获得一个路径名参数,它可以打开并读取cmd2
from 的输出,就像 Bash 进程替换会发生的那样main_command <(cmd2)
:事实上,这可能会/dev/fd/63
作为参数给出,否则在内部进行非常相似的处理。对于完整的命令
我们有嵌套的子shell:那是因为我们想要制作标准输入的两个副本,但它是两个不同的标准输入:一个是 的输出
cmd1
,它在通过管道进入更大的子shell 后被放入 FD 3,另一个是输出ofcmd2
,在通过管道进入最里面的子壳后被放入 FD 4。这两个0
s 都指标准输入,但是每个命令的标准输入是不同的,因为我们有不同的东西通过管道输入。我认为这是问题中最令人困惑的部分。每个命令(这里是每个子shell)都有自己的标准输入,从
cmd1
or管道输入cmd2
,并且唯一的标准输入流被别名为3
or4
。那些打开的文件描述符被下一层的子shell和子命令继承,所以/dev/fd/3
在最里面的命令指的是它在外面做的同样的事情,即使标准输入现在指向别的东西。外括号不是严格要求的,尽管它们使某些命令更加健壮,并且可能是一个好习惯。内部是:那些用于创建一个新的子进程,该子进程可以在其中拥有自己的一组重定向,并通过管道输入自己的标准输入流。
最里面的重定向实际上是多余的:
cmd2 | main_command /dev/fd/3 /dev/stdin
也可以工作,因为没有对标准输入进行进一步的更改。直接解决您的问题:
开箱是到目前为止的整个帖子。
括号创建一个子shell,一个独立的shell进程,可以像任何其他命令一样使用,包括将输入通过管道输入,但可以在其中执行普通的shell操作,例如重定向。
部分。
4<&0
说文件描述符 4 将指向标准输入,重要的是现在称为标准输入- 而不是标准输入的概念。/dev/fd/4
是“一切都是文件意义”中的“文件”,但更具体地说,它是一个路径名,当打开它时,它会将您的 FD 4 交还给您。