当我运行该命令时type type
,这会给我一个标准输出结果。
所以,现在我尝试这个命令:
type type > abc.txt
它将标准输出重定向到abc.txt
.因此,如果此命令在同一进程(bash 本身)上执行,则文件描述符 1 指向abc.txt
.之后,每当我使用 run 命令时,这些结果都应该转到 file abc.txt
,因为文件描述符 1 指向abc.txt
.
但是,结果始终会输出到标准输出。这是否意味着 shell 内置程序在分叉进程下运行?
答案1
通常情况下,要重定向命令,shell 所要做的就是分叉一个子进程,仅在子进程中进行重定向(父文件描述符不受影响),然后在其中执行命令(而父母只是等待孩子)。
对于内置命令(或复合命令,或函数...),没有 fork 或 exec。然而,在重定向的内置命令终止后,文件描述符又恢复到之前的状态。
为此,shell 只是在执行重定向之前将重定向的文件描述符的副本保存到另一个文件描述符,并使用 O_CLOEXEC 标志标记该文件描述符(以防内置命令最终执行类似eval
或command
的命令),并且当builtin 返回,shell 恢复文件描述符。
例如,如果您运行以下命令,您会看到:
strace sh -c 'echo test > /dev/null; :'
您将看到(仅包含相关条目):
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 中,重定向复合命令确实会导致分叉(如在中a=0; { a=1; echo "$a"; } >&2; echo "$a"
给出1
then 0
)。要解决这个问题,您实际上必须手动执行上述操作:
代替
while cmd1; do cmd2; i=`expr "$i" + 1`; done > file
你必须这样做:
exec 3>&1 > file
while cmd1 3>&-; do cmd2 3>&-; i=`expr "$i" + 1 3>&-`; done
exec >&3 3>&-
(3>&-
每个命令都有一个,因为 fd 没有 O_CLOEXEC 标志)。
在 Bourne shell 的早期版本中,您无法重定向内置命令。在模拟 pdp11 上的 Unix V7 上:
$ eval a=1 > /tmp/x
illegal io
$ read a < /etc/passwd
illegal io
答案2
输出重定向
文件描述符1
代表stdout
标准输出流。当在 中使用输出重定向时type type > abc.txt
,shell 打开文件abc.txt
进行写入,并修改文件描述符1
,使其指向打开的文件而不是终端设备。
但是,此重定向仅适用于当前命令正在执行所以这不是暗示该命令在分叉进程(或子 shell)中执行。
持久重定向
如果您希望重定向持续存在,您可以使用exec
内置的 shell 来修改文件描述符,例如,要重定向连续命令的标准输出,请运行以下命令。
exec >abc.txt
运行此命令时请小心,因为如果所有命令输出都重定向到文件而不是终端设备,您的 shell 会话将难以使用。您可以通过将文件描述符重定向到(文件描述符)stdout
指向的同一设备来将文件描述符恢复到终端输出设备:stderr
2
exec >&2
相关资源
欲了解更多详情,请参阅:
答案3
命令完成后,输入文件将立即关闭,使其可供其他进程写入,文件描述符将被释放,并且子进程将终止。