manpagez: man pages & more
info autoconf
Home | html | info | man

File: autoconf.info,  Node: File Descriptors,  Next: Signal Handling,  Prev: Here-Documents,  Up: Portable Shell

11.5 File Descriptors
=====================

Most shells, if not all (including Bash, Zsh, Ash), output traces on
stderr, even for subshells.  This might result in undesirable content if
you meant to capture the standard-error output of the inner command:

     $ ash -x -c '(eval "echo foo >&2") 2>stderr'
     $ cat stderr
     + eval echo foo >&2
     + echo foo
     foo
     $ bash -x -c '(eval "echo foo >&2") 2>stderr'
     $ cat stderr
     + eval 'echo foo >&2'
     ++ echo foo
     foo
     $ zsh -x -c '(eval "echo foo >&2") 2>stderr'
     # Traces on startup files deleted here.
     $ cat stderr
     +zsh:1> eval echo foo >&2
     +zsh:1> echo foo
     foo

One workaround is to grep out uninteresting lines, hoping not to remove
good ones.

   If you intend to redirect both standard error and standard output,
redirect standard output first.  This works better with AIX, since its
shell mishandles tracing if standard error is redirected first:

     $ sh -x -c ': 2>err >out'
     + :
     + 2> err $ cat err
     1> out

   Don’t try to redirect the standard error of a command substitution.
It must be done _inside_ the command substitution.  When running ‘: `cd
/zorglub` 2>/dev/null’ expect the error message to escape, while ‘: `cd
/zorglub 2>/dev/null`’ works properly.

   On the other hand, some shells, such as Solaris or FreeBSD ‘/bin/sh’,
warn about missing programs before performing redirections.  Therefore,
to silently check whether a program exists, it is necessary to perform
redirections on a subshell or brace group:
     $ /bin/sh -c 'nosuch 2>/dev/null'
     nosuch: not found
     $ /bin/sh -c '(nosuch) 2>/dev/null'
     $ /bin/sh -c '{ nosuch; } 2>/dev/null'
     $ bash -c 'nosuch 2>/dev/null'

   FreeBSD 6.2 sh may mix the trace output lines from the statements in
a shell pipeline.

   It is worth noting that Zsh (but not Ash nor Bash) makes it possible
in assignments though: ‘foo=`cd /zorglub` 2>/dev/null’.

   Some shells, like ‘ash’, don’t recognize bi-directional redirection
(‘<>’).  And even on shells that recognize it, it is not portable to use
on fifos: POSIX does not require read-write support for named pipes, and
Cygwin does not support it:

     $ mkfifo fifo
     $ exec 5<>fifo
     $ echo hi >&5
     bash: echo: write error: Communication error on send

Furthermore, versions of ‘dash’ before 0.5.6 mistakenly truncate regular
files when using ‘<>’:

     $ echo a > file
     $ bash -c ': 1<>file'; cat file
     a
     $ dash -c ': 1<>file'; cat file
     $ rm a

   Solaris 10 ‘/bin/sh’ executes redirected compound commands in a
subshell, while other shells don’t:

     $ /bin/sh -c 'foo=0; { foo=1; } 2>/dev/null; echo $foo'
     0
     $ ksh -c 'foo=0; { foo=1; } 2>/dev/null; echo $foo'
     1
     $ bash -c 'foo=0; { foo=1; } 2>/dev/null; echo $foo'
     1

   Solaris 10 ‘sh’ will try to optimize away a ‘:’ command (even if it
is redirected) in a loop after the first iteration, or in a shell
function after the first call:

     $ for i in 1 2 3 ; do : >x$i; done
     $ ls x*
     x1
     $ f () { : >$1; }; f y1; f y2; f y3;
     $ ls y*
     y1

As a workaround, ‘echo’ or ‘eval’ can be used.

   When running a subsidiary program be careful if descriptor 0 is not
open for reading or decriptors 1 and 2 are not open for writing, as the
subsidiary program might not behave as expected.  In particular, don’t
rely on file descriptors 0, 1, and 2 remaining closed in a subsidiary
program.  If any of these descriptors is closed, the operating system
may open an unspecified file for the descriptor in the newly created
process.

   If you want a file descriptor above 2 to be inherited into a child
process, then you must use redirections specific to that command or a
containing subshell or command group, rather than relying on ‘exec’ in
the shell.  In ‘ksh’ as well as HP-UX ‘sh’, file descriptors above 2
which are opened using ‘exec N>file’ are closed by a subsequent ‘exec’
(such as that involved in the fork-and-exec which runs a program or
script):

     $ echo 'echo hello >&5' >k
     $ /bin/sh -c 'exec 5>t; ksh ./k; exec 5>&-; cat t
     hello
     $ bash -c 'exec 5>t; ksh ./k; exec 5>&-; cat t
     hello
     $ ksh -c 'exec 5>t; ksh ./k; exec 5>&-; cat t
     ./k[1]: 5: cannot open [Bad file number]
     $ ksh -c '(ksh ./k) 5>t; cat t'
     hello
     $ ksh -c '{ ksh ./k; } 5>t; cat t'
     hello
     $ ksh -c '5>t ksh ./k; cat t
     hello

   Don’t rely on duplicating a closed file descriptor to cause an error.
With Solaris 10 ‘/bin/sh’, failed duplication is silently ignored, which
can cause unintended leaks to the original file descriptor.  In this
example, observe the leak to standard output:

     $ bash -c 'echo hi >&3' 3>&-; echo $?
     bash: 3: Bad file descriptor
     1
     $ /bin/sh -c 'echo hi >&3' 3>&-; echo $?
     hi
     0

   Fortunately, an attempt to close an already closed file descriptor
will portably succeed.  Likewise, it is safe to use either style of
‘N<&-’ or ‘N>&-’ for closing a file descriptor, even if it doesn’t match
the read/write mode that the file descriptor was opened with.

   DOS variants cannot rename or remove open files, such as in ‘mv foo
bar >foo’ or ‘rm foo >foo’, even though this is perfectly portable among
POSIX hosts.

   A few ancient systems reserved some file descriptors.  By convention,
file descriptor 3 was opened to ‘/dev/tty’ when you logged into Eighth
Edition (1985) through Tenth Edition Unix (1989).  File descriptor 4 had
a special use on the Stardent/Kubota Titan (circa 1990), though we don’t
now remember what it was.  Both these systems are obsolete, so it’s now
safe to treat file descriptors 3 and 4 like any other file descriptors.

   On the other hand, you can’t portably use multi-digit file
descriptors.  ‘dash’ and Solaris ‘ksh’ don’t understand any file
descriptor larger than ‘9’:

     $ bash -c 'exec 10>&-'; echo $?
     0
     $ ksh -c 'exec 9>&-'; echo $?
     0
     $ ksh -c 'exec 10>&-'; echo $?
     ksh[1]: exec: 10: not found
     127
     $ dash -c 'exec 9>&-'; echo $?
     0
     $ dash -c 'exec 10>&-'; echo $?
     exec: 1: 10: not found
     2

© manpagez.com 2000-2026
Individual documents may contain additional copyright information.