On 23/08/2023 02:59, Greg Wooledge wrote:
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.

At first glance it is documented and effect of {fd}>&- is limited to the function call

info "(bash) Redirections"
If {VARNAME} is supplied, the redirection persists
beyond the scope of the command, allowing the shell programmer to manage
the file descriptor's lifetime manually.  The 'varredir_close' shell
option manages this behavior (*note The Shopt Builtin::).

info "(bash) The Shopt Builtin"
     'varredir_close'
          If set, the shell automatically closes file descriptors
          assigned using the '{varname}' redirection syntax (*note
          Redirections::) instead of leaving them open when the command
          completes.

However the redirection does not persist if an external executable is called instead of a BASH function. Perhaps it should be discussed with BASH developers.

foo() {
    sh -c 'echo "internal $1" /proc/self/fd/*' foo "$1"
}
bar() {
    local fd
    sh -c "echo 'external before' /proc/self/fd/*"
sh -c "echo 'external closed' /proc/self/fd/*" {fd}>&1 1>&2 2>&${fd} {fd}>&-
    sh -c "echo 'external after ' /proc/self/fd/*"
    echo "fd: $fd"
    sh -c "echo 'external before' /proc/self/fd/*"
    sh -c "echo 'external open  ' /proc/self/fd/*" {fd}>&1 1>&2 2>&${fd}
    sh -c "echo 'external after ' /proc/self/fd/*"
    echo "fd: $fd"
    echo
    sh -c "echo 'internal before' /proc/self/fd/*"
    foo "closed" {fd}>&1 1>&2 2>&${fd} {fd}>&-
    sh -c "echo 'internal after ' /proc/self/fd/*"
    echo "fd: $fd"
    if [ -n "$fd" ]; then exec {fd}>&-; fd=; fi
    sh -c "echo 'internal before' /proc/self/fd/*"
    foo "open  " {fd}>&1 1>&2 2>&${fd}
    sh -c "echo 'internal after ' /proc/self/fd/*"
    echo "fd: $fd"
    if [ -n "$fd" ]; then exec {fd}>&-; fd=; fi
}
bar

Pay attention to {fd} that is is 10, other may be ignored, in particular /proc/self/fd opened by readdir is 3, some inotify is 20 below.

external before /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 external closed /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 external after /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3
fd:
external before /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 external open /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/10 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 external after /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3
fd:

internal before /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 internal closed /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 internal after /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/10 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3
fd: 10
internal before /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 internal open /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/10 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3 internal after /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/10 /proc/self/fd/2 /proc/self/fd/20 /proc/self/fd/3
fd: 10

GNU bash, version 5.2.15(1)-release (x86_64-pc-linux-gnu)

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

I have not tested if "trap" is more robust in the presence of "set -e -o pipefail". I know, there are enough pitfalls with this options.

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

You mentioned the limitation: a spare file descriptor must be known.

Reply via email to