了解 dash 中的管道和重定向

了解 dash 中的管道和重定向

有人问如何将两个命令的输出作为文件传递给另一个命令,他们得到了回答以下。

( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )

我需要把这个拆开。

假设我有一个文本文件some_file,我希望将其作为输入传递给main_command.main_command采用两个文件作为输入。如果我想main_commandsome_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. 如何解压问题开头的命令?
  2. 括号有什么作用?
  3. 我对更简单命令的解释正确吗?

编辑:我应该说如果我的逻辑是正确的,那么就没有必要回答1。

答案1

这是一个相当复杂的命令。我在最后直接回答了您的问题,但在此之前所有这些都是解压命令本身。我已经尽力做到全面,因此有些地方可能比您需要的更详细。

括号创建一个子 shell:

( x y z )

表示从当前 shell 中派生一个新 shell,并x y z在其中执行(然后返回到当前 shell)。子 shell 继承了当前 shell 的所有内容,但它是一个单独的进程:这意味着它可以通过管道输入输入,并且可以在内部进行自己的环境更改,而不会影响父级。

每个打开的文件都有一个数字“文件描述符”与之相关。本文中的“文件”包括任何类型的输入或输出流,包括实际文件、套接字和标准 I/O 流。这些数字是可以直接与Cread函数来识别您正在谈论的流,并使用操作系统提供的相应系统调用以及所有其他 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与它所在的子 shell 具有相同的文件描述符 4,因此/dev/fd/4也可以在那里工作。

cmd2 | x运行cmd2,并将其标准输出连接到 的标准输入(或 FD 0)x。在你的命令中,x是子shell表达式。


我们的总体指挥

cmd2 | ( main_command /dev/fd/4 ) 4<&0

然后有三个主要部分:

  1. 运行cmd2并将其输出传输到( main_command /dev/fd/4 ) 4<&0.
  2. 为由(标准输入) of4标识的流指定另一个名称。0( main_command /dev/fd/4 )
  3. main_command作为参数运行/dev/fd/4,它将(大概)作为文件打开并从中读取,获取cmd2.

最终效果是main_command获取一个路径名参数,它可以打开并读取 from 的输出cmd2,与 Bash 进程替换完全一样main_command <(cmd2):事实上,这可能会/dev/fd/63作为参数给出,否则在内部的处理过程非常相似。


对于完整的命令

( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )

我们有嵌套的子shell:那是因为我们想要制作标准输入的两个副本,但它是两个不同的标准输入:一个是 的输出cmd1,在通过管道输送到较大的子 shell 后放入 FD 3,另一个是 的输出cmd2,在通过管道输送到最里面的子 shell 后放入 FD 4。这两个0命令都引用标准输入,但每个命令的标准输入是不同的,因为我们通过管道输入了不同的内容。

我认为这是这个问题中最令人困惑的部分。每个命令 - 这里是每个子 shell - 都有它自己的cmd1标准输入,从或管道输入cmd2,并且该唯一的标准输入流被别名为34。这些打开的文件描述符由下一层子 shell 和子命令继承,因此/dev/fd/3在最里面的命令中指的是它在外部执行的相同操作,即使标准输入现在指向其他内容。

外括号并不是严格必需的,尽管它们对于某些命令来说稍微更健壮,并且可能是一个很好的做法。内部的是:它们用于创建一个新的子进程,该子进程可以在其中拥有自己的一组重定向,以及通过管道输入自己的标准输入流。

最里面的重定向实际上是多余的:cmd2 | main_command /dev/fd/3 /dev/stdin也可以工作,因为没有对标准输入进行进一步的更改。


直接解决您的问题:

  1. 如何解压问题开头的命令?

    拆包就是到目前为止的整个帖子。

  2. 括号有什么作用?

    括号创建一个子 shell,这是一个独立的 shell 进程,可以像任何其他命令一样使用,包括将输入通过管道传输到其中,但可以在内部执行普通的 shell 操作,例如重定向。

  3. 我对更简单命令的解释正确吗?

    部分地。4<&0说文件描述符 4 将指向 stdin,重要的是现在称为 stdin- 不是标准输入的概念。/dev/fd/4是“一切都是文件意义上的”中的“文件”,但更具体地说,它是一个路径名,打开时会将您带回 FD 4。

相关内容