"Bad file descriptor" with Bash; how to designate descriptors?

"Bad file descriptor" with Bash; how to designate descriptors?

Post by Bradley Rosse » Sat, 05 Jan 2002 00:40:12



I have a trivial script which tails multiple files by
spawning a 'tail' process for each file and duping the
output into one file descriptor which is then tailed:

  for f in $*; do
        tail -1f $f >&3 &
  done

  exec <&3
  tail -f

This works fine under Sun ksh, but I've just tried to run
it now with Bash under Linux 2.4.16, only to have each
command using file descriptor 3 fail with a

  'Bad file descriptor'

error.

This has caught me completely unawares.  The bash manpage
says that

  "If the digits in word do not specify a file descriptor open
  for output, a redirection error occurs"

How do I open file descriptor 3 for output?  Or, if I do
something like

  tail -1f file > /dev/null &

how can I find out what file descriptor is opened so I can
direct the output of further tail commands to the same
descriptor?  What's the magic that bash needs to be able to
do something like this?

Many thanks,

Brad

 
 
 

"Bad file descriptor" with Bash; how to designate descriptors?

Post by John DuBo » Sat, 05 Jan 2002 06:03:02




Quote:>I have a trivial script which tails multiple files by
>spawning a 'tail' process for each file and duping the
>output into one file descriptor which is then tailed:

>  for f in $*; do
>        tail -1f $f >&3 &
>  done

>  exec <&3
>  tail -f

This isn't doing what you think it is.  A bug in sh (which is also present in
the one I'm using) is causing it to respond to an attempt at dup'ing an fd that
isn't open (3) by dup'ing fd 1 instead.  >&3 in your script is therefore a
no-op, equivalent to 1>&1.  The 'exec <&3; tail -f' at the bottom doesn't do
anything except cause your script to pause until you interrupt it; try your
script without it to see.  Then do:

    for f in $*; do
          tail -1f $f &
    done
    wait

which will work in bash as well and does what you want.  You don't need the
extra 'tail' to conglomerate the output - it's already conglomerated onto a
single fd by virtue of the dup'ing.

It looks like you're thinking of file descriptors as being equivalent to pipes,
with output to a given fd in a script being available to other programs by
redirecting input from that fd, but this is not the case - a file descriptor
doesn't refer to a pipe unless you make it refer to a pipe.  If you did want to
pipe all of that tail output into another process, you'd do it like this:

    for f in $*; do
          tail -1f $f &
    done | my-output-processor

Or to get a more generally available fd in ksh:

    exec 3>&1
    my-output-processor >&3 |&
    for f in $*; do
          tail -1f $f &
    done >&p
    more-output >&p

Under some OSes, you can make named pipes, which give an effect closest to the
way you were trying to set it up:

    mknod /tmp/mypipe p
    for f in $*; do
          tail -1f $f &
    done >/tmp/mypipe
    more-output >/tmp/mypipe &
    my-output-processor </tmp/mypipe

What differentiates these from the way you tried to do it is that all of these
do set up pipes.

        John
--