当我使用内置命令时,shell 是否会分叉?

A.C*_*Cho 7 shell io-redirection

当我运行命令时type type,这会给我一个标准输出的结果。

所以,现在我试试这个命令:

type type > abc.txt
Run Code Online (Sandbox Code Playgroud)

它将标准输出重定向到abc.txt. 因此,如果此命令在同一进程(bash 本身)上执行,则文件描述符 1 指向abc.txt. 之后,每当我使用 run 命令时,这些结果都应该转到 file abc.txt,因为文件描述符 1 指向abc.txt.

但是,结果总是进入标准输出。这是否意味着 shell 内置程序在分叉进程下运行?

Sté*_*las 20

通常你是对的,要重定向命令,shell 所要做的就是派生一个子进程,只在子进程中进行重定向(父文件描述符不受影响),然后在那里执行命令(而父母只是在等待孩子)。

对于内置命令(或复合命令,或函数...),没有 fork 或 exec。然而,在重定向的内置命令终止后,文件描述符又恢复到以前的状态。

要做到这一点,shell 只是在执行重定向之前将重定向的文件描述符的副本保存到另一个文件描述符,用 O_CLOEXEC 标志标记该文件描述符(以防内置命令最终执行类似evalcommand将要执行的命令),以及当内置返回,shell 恢复文件描述符。

你可以看到,如果你运行例如:

strace sh -c 'echo test > /dev/null; :'
Run Code Online (Sandbox Code Playgroud)

您会看到(仅包含相关条目):

  • open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3: 打开要重定向到的文件。
  • fcntl(1, F_DUPFD, 10) = 10:将原始标准输出复制到第一个可用的 fd >= 10(此处为 10)。
  • fcntl(10, F_SETFD, FD_CLOEXEC) = 0: 在其上设置 O_CLOEXEC 标志
  • dup2(3, 1) = 1: 使重定向文件标准输出
  • close(3) = 0: 不再需要
  • write(1, "test\n", 5) = 5:echo 运行并在其标准输出上写入“test\n”(现在重定向到 /dev/null)。
  • dup2(10, 1) = 1: 恢复标准输出
  • close(10) = 0: 不再需要关闭 fd 10。

请注意,在 Bourne shell 中,重定向复合命令确实会导致 fork(就像 ina=0; { a=1; echo "$a"; } >&2; echo "$a"会给你1then 0)。要解决这个问题,您实际上必须手动执行上述操作:

代替

while cmd1; do cmd2; i=`expr "$i" + 1`; done > file
Run Code Online (Sandbox Code Playgroud)

你必须这样做:

exec 3>&1 > file
while cmd1 3>&-; do cmd2 3>&-; i=`expr "$i" + 1 3>&-`; done
exec >&3 3>&-
Run Code Online (Sandbox Code Playgroud)

3>&-每个命令都有一个,因为 fd 没有 O_CLOEXEC 标志)。

在 Bourne shell 的早期版本中,您无法重定向内置函数。在模拟 pdp11 上的 Unix V7 上:

$ eval a=1 > /tmp/x
illegal io
$ read a < /etc/passwd
illegal io
Run Code Online (Sandbox Code Playgroud)


Ant*_*gan 10

输出重定向

文件描述符1代表stdout,标准输出流。在 中使用输出重定向时type type > abc.txt,shell 打开文件abc.txt进行写入并1修改文件描述符,使其指向打开的文件而不是终端设备。

然而,该重定向仅适用于当前命令正在执行因此这并意味着在分叉处理(或子shell)的命令执行。

持续重定向

如果您希望重定向持续存在,您可以使用exec内置的shell 来修改文件描述符,例如,为连续命令重定向标准输出,运行以下命令。

exec >abc.txt
Run Code Online (Sandbox Code Playgroud)

小心运行它,因为如果所有命令输出都被重定向到一个文件而不是你的终端设备,你的 shell 会话将很难使用。您可以stdout通过将文件描述符重定向到stderr(file descriptor 2)指向的同一设备来将文件描述符还原到终端输出设备:

exec >&2
Run Code Online (Sandbox Code Playgroud)

相关资源

有关更多详细信息,请参阅: