Tra*_*iet 12 bash file-descriptors
我理解文件描述符(或文件处理程序)是Linux 系统中的一种文件 IO 技术。
我也知道每个进程都有 3 个标准流(即 stdin、stdout 和 stderr),它们由描述符从 0 到 3 的文件表示。
但是,我注意到我检查的所有进程lsof -p <pid>都有一个255具有读取权限的额外文件描述符。
从这个答案中,我了解到这个特性是特定于Bash shell 的,但是答案和引用的来源都没有真正解释这个文件描述符的用途。
我的问题:
ImH*_*ere 17
对于您问题的最后一部分:
来自man bash:
使用大于 9 的文件描述符的重定向应谨慎使用,因为它们可能与 shell 内部使用的文件描述符冲突。
因此,如果您的意思是使用该号码创建一个新的 fd,那么答案是否定的。
如果您的意思是用作:“写入该 fd”:
$ echo hello >/dev/fd/255"
Run Code Online (Sandbox Code Playgroud)
或者从中阅读:
$ read a </dev/fd/255
abc
$ echo "$a"
abc
Run Code Online (Sandbox Code Playgroud)
答案是肯定的。
但是,可能应该更好(独立于 shell)/dev/tty用于访问tty.
作为与 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%
Run Code Online (Sandbox Code Playgroud)
从邮件列表:
Fd 255 在内部用作与 tty 的连接,因此它不会干扰使用 exec 重新定位 fds。出于同样的原因,Bash 在处理进程替换 `<(foo)' 时也会分配高 fds。
安德烈亚斯·施瓦布
mos*_*svy 11
该255文件描述符是控制 tty 的打开句柄,仅bash在以交互模式运行时使用。
它允许您stderr在主 shell 中重定向,同时仍然允许作业控制运行(即能够使用 ^C 终止进程,使用 ^Z 中断它们等)。
例子:
$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000
Run Code Online (Sandbox Code Playgroud)
如果您在类似 的 shell 中尝试这样做ksh93,它只是使用文件描述符 2 作为对控制终端的引用,则该sleep进程将不受 ^C 和 ^Z 的影响,并且必须从另一个窗口/会话中终止。这是因为 shell 将无法将 的进程组设置sleep为终端中的前台进程组tcsetgrp(),因为文件描述符 2 不再指向终端。
这不是bash特定的,它也用于dashand zsh,只是描述符没有移动那么高(通常是 10)。
zsh 还将使用该 fd 来回显提示和用户输入,因此只需执行以下操作:
$ exec 2>/tmp/err
$
Run Code Online (Sandbox Code Playgroud)
它与bash读取脚本和设置管道时使用的文件句柄无关(它们也用相同的功能被排除在外 - move_to_high_fd()),正如其他答案和评论中所建议的那样。
bash使用如此大的数字是为了允许 fds 大于9用于壳内重定向(例如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);
...
}
Run Code Online (Sandbox Code Playgroud)
如果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)
Run Code Online (Sandbox Code Playgroud)
shell_tty 然后用于
使用tc[sg]etpgrpin获取和设置前台进程组jobs.c:maybe_give_terminal_to(),jobs.c:set_job_control()以及jobs.c:give_terminal_to()
获取和设置termios(3)在PARAMSjobs.c:get_tty_state()和jobs.c:set_tty_state()
使用ioctl(TIOCGWINSZ)in获取终端窗口大小lib/sh/winsize.c:get_new_window_size()。
move_to_high_fd()通常与bash(脚本文件、管道等)使用的所有临时文件描述符一起使用,因此在谷歌搜索中突出显示的大多数评论中的混淆。
内部使用的文件描述符bash,包括shell_tty都设置为close-on-exec,所以它们不会泄露给命令。