该255
文件描述符是控制tty的打开句柄,仅bash
在以交互方式运行时使用。
它允许您stderr
在主外壳中重定向,同时仍然允许作业控制起作用(即,能够使用^ C终止进程,使用^ Z中断进程,等等)。
例:
$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000
如果您在像这样的shell中尝试该操作ksh93
,它只是使用文件描述符2作为对控制终端的引用,则该sleep
进程将不受^ C和^ Z的影响,并且必须从另一个窗口/会话中删除。那是因为shell不能通过sleep
将该终端的进程组设置为终端中的前台进程tcsetgrp()
,因为文件描述符2不再指向终端。
这不是bash
特定的,它也用在dash
和中zsh
,只是描述符没有被移到那么高(通常是10)。
zsh
还将使用该fd回显提示和用户输入,因此只需执行以下操作:
$ exec 2>/tmp/err
$
正如在其他答案和评论中所建议bash
的那样,这与读取脚本和设置管道时使用的文件句柄无关(它们也用相同的功能-进行了重复处理move_to_high_fd()
)。
bash
为了允许fds大于9
外壳内重定向(例如exec 87<filename
)而使用了如此大量的代码;其他Shell不支持。
您可以自己使用该文件句柄,但是这样做没有任何意义,因为您可以通过使用任何命令在同一命令终端上获得一个句柄... < /dev/tty
。
bash的源代码分析:
在中bash
,控制终端的文件描述符存储在shell_tty
变量中。如果外壳是交互式的,则jobs.c:initialize_job_control()
通过从stderr
(如果stderr
附加到终端)对其/dev/tty
进行复制或直接打开来初始化该变量(在启动时或执行失败后)。与general.c:move_to_high_fd()
:
int
initialize_job_control (force)
int force;
{
...
if (interactive == 0 && force == 0)
{
...
}
else
{
shell_tty = -1;
/* If forced_interactive is set, we skip the normal check that stderr
is attached to a tty, so we need to check here. If it's not, we
need to see whether we have a controlling tty by opening /dev/tty,
since trying to use job control tty pgrp manipulations on a non-tty
is going to fail. */
if (forced_interactive && isatty (fileno (stderr)) == 0)
shell_tty = open ("/dev/tty", O_RDWR|O_NONBLOCK);
/* Get our controlling terminal. If job_control is set, or
interactive is set, then this is an interactive shell no
matter where fd 2 is directed. */
if (shell_tty == -1)
shell_tty = dup (fileno (stderr)); /* fd 2 */
if (shell_tty != -1)
shell_tty = move_to_high_fd (shell_tty, 1, -1);
...
}
如果shell_tty
还不是控制tty,则将其设置为:
/* If (and only if) we just set our process group to our pid,
thereby becoming a process group leader, and the terminal
is not in the same process group as our (new) process group,
then set the terminal's process group to our (new) process
group. If that fails, set our process group back to what it
was originally (so we can still read from the terminal) and
turn off job control. */
if (shell_pgrp != original_pgrp && shell_pgrp != terminal_pgrp)
{
if (give_terminal_to (shell_pgrp, 0) < 0)
shell_tty
然后习惯
获取和设置前台进程组tc[sg]etpgrp
中jobs.c:maybe_give_terminal_to()
,jobs.c:set_job_control()
和jobs.c:give_terminal_to()
获取和设置termios(3)
在PARAMS jobs.c:get_tty_state()
和jobs.c:set_tty_state()
以获得终端窗口的大小ioctl(TIOCGWINSZ)
在lib/sh/winsize.c:get_new_window_size()
。
move_to_high_fd()
通常与bash
(脚本文件,管道等)使用的所有临时文件描述符一起使用,因此大多数注释中的混淆会在Google搜索中突出显示。
bash
包括shell_tty
在内部使用的文件描述符都设置为close-on-exec,因此它们不会泄漏给命令。