See, this is why I hate using bash extensions.  So many weird corner
cases and surprises....

As it turns out, the answer I gave yesterday was only partially correct.
The "pure sh" solution is fine, but I offered this bash alternative:

    cmd {fd}>&1 1>&2 2>&$fd {fd}>&-

This doesn't actually work as expected.  It fails to close the FD in the
final redirection.

What does it actually do?  Hell if I know.  I literally can't figure it
out.

Consider this function:

foo() { echo out; echo err >&2; }

Simple enough, right?  It writes "out" to stdout, and "err" to stderr.  We
can use this to see what's going where, while we play games with
redirections.

Next, we write a simple wrapper, which should in theory swap stdout and
stderr:

bar() { 
    local fd
    foo {fd}>&1 1>&2 2>&${fd} {fd}>&-
}

Running this appears to gives the correct results -- outputs go to the
right places -- but it leaves the temporary FD open.  The final
redirection to close it doesn't work.

But *this* one works:

bar() { 
    local fd
    foo {fd}>&1 1>&2 2>&${fd}
    exec {fd}>&-
}

Why?  Again, I do not know.  My theories all fail to account for all of
the observed results.  And, of course, if we wanted to preserve the exit
status of foo, the second wrapper script doesn't do that.  It'll need
another variable to hold that:

bar() { 
    local fd rc
    foo {fd}>&1 1>&2 2>&${fd}
    rc=$?
    exec {fd}>&-
    return "$rc"
}

At this point, the "pure sh" version looks a whole lot simpler,
doesn't it?

THIS is why I have a wiki devoted to bash and its problems.

Reply via email to