在 Bash 中,文件描述符 255 是做什么用的,我可以使用它吗?

在 Bash 中,文件描述符 255 是做什么用的,我可以使用它吗?

我明白文件描述符(或文件处理程序)是文件IO技术在Linux系统中。

我还知道每个进程都有 3 个标准流(即 stdin、stdout 和 stderr),它们由描述符从 0 到 3 的文件表示。

但是,我注意到我检查的所有进程都有一个具有读取权限的lsof -p <pid>额外文件描述符。255

这个答案,我了解到这个功能是特定于bash shell,但是答案和引用的源都没有真正解释这个文件描述符的用途。

我的问题:

  1. 255文件描述符有什么用?
  2. 我可以在我的 Bash 脚本中使用它吗?还是它只是一个不应该手动使用/操作的内部工作机制?

答案1

对于你问题的最后一部分:

我可以使用它吗?

man bash

使用大于 9 的文件描述符的重定向应谨慎使用,因为它们可能与 shell 内部使用的文件描述符发生冲突。

因此,如果您的意思是使用该编号创建一个新的文件描述符,那么答案是否定的。

如果您的意思是使用 as:“写入该 fd”:

$ echo hello >/dev/fd/255"

或者从中读取:

$ read a </dev/fd/255
abc
$ echo "$a"
abc

答案是肯定的。
但是,也许,使用/dev/tty它来访问tty.

文件描述符 255 是做什么用的?

作为与 tty 的替代连接,以防 fd 1 ( /dev/stdout) 和 fd 0 ( /dev/stdin) 被阻止。

更多详情

其他 shell 可能使用不同的数字(例如 zsh 中的 10)

$ zsh
mail% ls -l /proc/self/fd /proc/$$/fd/* &
[1] 3345
mail% lrwx------ 1 isaac isaac 64 Oct 14 09:46 /proc/3250/fd/0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/10 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/2 -> /dev/pts/2

/proc/self/fd:
total 0
lrwx------ 1 isaac isaac 64 Oct 14 09:50 0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 2 -> /dev/pts/2
lr-x------ 1 isaac isaac 64 Oct 14 09:50 3 -> /proc/3345/fd

[1]  + done       ls -l /proc/self/fd /proc/$$/fd/*
mail% 

邮件列表:

Fd 255 在内部用作与 tty 的连接,因此它不会干扰使用 exec 重新定位 fds。出于同样的原因,Bash 在处理进程替换“<(foo)”时也会分配高 fd。
安德烈亚斯·施瓦布

答案2

该文件描述符是控制 tty 的打开句柄,仅在以交互模式运行255时使用。bash

它允许您在主 shell 中重定向stderr,同时仍然允许作业控制运行(即能够使用 ^C 终止进程,使用 ^Z 中断它们等)。

例子:

$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000

如果您在像 这样的 shell 中尝试这样做ksh93,它只是使用文件描述符 2 作为对控制终端的引用,则该sleep进程将不受 ^C 和 ^Z 的影响,并且必须从另一个窗口/会话中终止。这是因为 shell 将无法使用 来将 进程组 设置sleep为终端中的前台进程组tcsetgrp(),因为文件描述符 2 不再指向终端。

这并不bash具体,它也用于dashzsh,只是描述符没有移动那么高(通常为 10)。

zsh还将使用该 fd 来回显提示和用户输入,因此只需执行以下操作即可:

$ exec 2>/tmp/err
$ 

正如其他答案和评论中所建议的那样,它与读取脚本和设置管道时使用的文件句柄无关bash(这些管道也使用相同的功能进行了复制 - )。move_to_high_fd()

bash使用如此大的数字是为了允许 fd 大于9与 shell 内重定向一起使用的值(例如exec 87<filename);其他 shell 不支持这一点。

您可以自己使用该文件句柄,但这样做没有什么意义,因为您可以获得该文件的句柄相同的在任何带有 的命令中控制终端... < /dev/tty

bash源码分析:

在 中bash,控制终端的文件描述符存储在shell_tty变量中。如果 shell 是交互式的,则通过jobs.c:initialize_job_control()stderr(如果stderr附加到终端)复制该变量或直接打开 来初始化该变量(在启动时或执行失败后) /dev/tty,然后再次复制到更高的 fd和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然后用于

  1. tc[sg]etpgrp使用injobs.c:maybe_give_terminal_to()和获取jobs.c:set_job_control()和设置前台进程组jobs.c:give_terminal_to()

  2. 获取并设置和termios(3)中的参数jobs.c:get_tty_state()jobs.c:set_tty_state()

  3. ioctl(TIOCGWINSZ)使用in获取终端窗口大小lib/sh/winsize.c:get_new_window_size()

move_to_high_fd()通常与(脚本文件、管道等)使用的所有临时文件描述符一起使用bash,因此在谷歌搜索中突出显示的大多数注释中都会出现混乱。

内部使用的文件描述符bash,包括shell_tty都设置为 close-on-exec,因此它们不会泄漏到命令中。

相关内容