255 file descriptor is an open handle to the controlling tty and is only used when
bash is run in interactive mode.
It allows you to redirect the
stderr in the main shell, while still allowing the job control to function (ie. be able to kill processes with ^C, interrupt them with ^Z, etc).
$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000
If you try that in a shell like
ksh93, which is simply using file descriptor 2 as a reference to the controlling terminal, the
sleep process will become immune to ^C and ^Z, and will have to be killed from another window/session. That's because the shell will not be able to set the process group of
sleep as the foreground one in the terminal with
tcsetgrp(), since file descriptor 2 no longer points to the terminal.
This is not
bash specific, it's also used in
zsh, only that the descriptor is not moved that high (it's usually 10).
zsh will also use that fd to echo prompts and user input, so simply the following will work:
$ exec 2>/tmp/err
It has nothing to do with the file handles
bash is using when reading scripts and setting up pipes (which are also dup'ed out of the way with the same function --
move_to_high_fd()), as it was suggested in other answers and comments.
bash is using such a large number in order to allow fds larger than
9 to be used with in-shell redirections (eg.
exec 87<filename); that's not supported in other shells.
You can use that file handle yourself, but there's little point in doing so, because you can get a handle to the very same controlling terminal in any command with
... < /dev/tty.
Source code analysis of bash:
bash, the file descriptor of the controlling terminal is stored in the
shell_tty variable. If the shell is interactive, that variable is initialized (at startup or after a failed exec) in
jobs.c:initialize_job_control() by dup'ing it from
stderr is attached to a terminal) or by directly opening
/dev/tty, and is then dup'ed again to a higher fd with
if (interactive == 0 && force == 0)
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 is not already the controlling tty, then it is made so:
/* 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 is then used to
get and set the foreground process group with
get and set the
termios(3) params in
get the terminal window size with
move_to_high_fd() is generally used with all the temporary file descriptors used by
bash (script files, pipes, etc), thence the confusion in most comments that appear prominently in google searches.
The file descriptors used internally by
shell_tty are all set to close-on-exec, so they won't be leaked to commands.