1

This is on Ubuntu 16.04, with bash 4.3.42 and tcsh 6.19

If a open a virtual console not being used by X and run bash, I see stdin, stdout, stderr and a dedicated file descriptor for the tty (apparently).

$ cd /dev/fd
$ ls
0 1 2 255
$ ls -al .
... .
... ..
... 0 -> /dev/tty3
... 1 -> /dev/tty3
... 2 -> /dev/tty3
... 255 -> /dev/tty3

If I'm using tcsh, I see five non-std{in, out, err} file descriptors pointing to the tty and std{in, out, err} are all mapped to /dev/null.

% cd /dev/fd
% ls -al
... .
... ..
... 0 -> /dev/null
... 1 -> /dev/null
... 15 -> /dev/tty3
... 16 -> /dev/tty3
... 17 -> /dev/tty3
... 18 -> /dev/tty3
... 19 -> /dev/tty3
... 2 -> /dev/null

Why would tcsh need so many file descriptors all pointing to the tty and what is the benefit of mapping 0,1, and 2 to /dev/null? Wouldn't that just mean that slightly more bookkeeping is needed when tcsh forks processes so they write to / read from the console?

cas
  • 78,579
Greg Nisbet
  • 3,076

2 Answers2

2

tcsh is organized differently from bash (no surprise). Both are old, and full of interesting quirks for the careful reader.

This difference is due to the way tcsh manages file descriptors. Unlike bash, it does not provide the script writer with a way to manipulate numbered file descriptors. The developers found it convenient to organize its file descriptors by moving the standard streams into a "saved" area (unused by real scripts), and when running commands, it duplicates those to commands (i.e., a subprocess), and closes them when the commands complete.

In the source code, sh.h has this chunk which explains the use of those file descriptors:

/*
 * The shell moves std in/out/diag and the old std input away from units
 * 0, 1, and 2 so that it is easy to set up these standards for invoked
 * commands.
 */
#define FSAFE   5               /* We keep the first 5 descriptors untouched */
#define FSHTTY  15              /* /dev/tty when manip pgrps */
#define FSHIN   16              /* Preferred desc for shell input */
#define FSHOUT  17              /* ... shell output */
#define FSHDIAG 18              /* ... shell diagnostics */
#define FOLDSTD 19              /* ... old std input */

For both shells, there are multiple links to the same "real" device in /dev/fd (for Linux, at least), because that is the way the pseudo-terminal driver is organized.

You will, by the way, get a different result if you run tcsh from another shell. But if your default shell is tcsh, likely you will see those file descriptors as described in the question.

Thomas Dickey
  • 76,765
0

This is almost certainly something in your tcsh configuration (e.g. ~/.login, ~/.cshrc /etc/csh.*) - look for anything that redirects stdin, stdout, stderr.

When I run tcsh on my system, I get:

$ tcsh
> ls -lF /dev/fd/
total 0
lrwx------ 1 cas cas 64 Jun 26 13:28 0 -> /dev/pts/29
lrwx------ 1 cas cas 64 Jun 26 13:28 1 -> /dev/pts/29
lrwx------ 1 cas cas 64 Jun 26 13:28 2 -> /dev/pts/29
lr-x------ 1 cas cas 64 Jun 26 13:28 3 -> /proc/16570/fd/
cas
  • 78,579
  • 2
    obligatory ps: csh and tcsh are evil....put on this earth to tempt people into bad scripting. – cas Jun 26 '16 at 03:34
  • According to Stephen Bourne, csh came about because Bill Joy asked him to add - I think - command history and job control to the original borne shell, Bourne told him he was too busy, so then Bill Joy created csh. – the_velour_fog Jun 26 '16 at 03:51
  • yes, well, the demonic forces behind csh would say that, wouldn't they? – cas Jun 26 '16 at 03:52
  • Yep. that's definitely right. If I do an exec tcsh -f then stdin and stdout and not redirected and I don't have any unnecessary open descriptors. I made the mistake of attempting to use tcsh a few months ago and now I can't get it out of my muscle memory :/ – Greg Nisbet Jun 26 '16 at 04:59