Testing if a file descriptor is valid
In ksh
(both AT&T and pdksh variants) or zsh
, you can do:
if print -nu3; then
echo fd 3 is writeable
fi
They won't write anything on that fd, but still check if the fd is writable (using fcntl(3, F_GETFL)
) and report an error otherwise:
$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing
(which you can redirect to /dev/null
).
With bash
, I think your only option is to check if a dup()
succeeds like in your approach, though that won't guarantee that the fd is writable (or call an external utility (zsh
/perl
...) to do the fcntl()
).
Note that in bash
(like most shells), if you use (...)
instead of {...;}
, that will fork an extra process. You can use:
if { true >&3; } 2<> /dev/null
instead to avoid the fork (except in the Bourne shell where redirecting compound commands always causes a subshell). Don't use :
instead of true
as that's a special builtin, so would cause the shell to exit when bash is in POSIX compliance mode.
You could however shorten it to:
if { >&3; } 2<> /dev/null
In the POSIX command
Application Usage description you'll find the following:
There are some advantages to suppressing the special characteristics of special built-ins on occasion. For example:
command exec > unwritable-file
does not cause a non-interactive script to abort, so that the output status can be checked by the script.
This is why you can just do:
if command >&3
then echo 3 is open >&3
else ! echo 3 is not open
fi 2<>/dev/null
Or...
{ command >&3
printf %s\\n%.0d string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null
Which will write string followed by a \n
ewline either to stdout or 3 and still pass on a non-zero exit status when 3 is not open because the math done on $?
winds up failing to convert the octal 08 to %decimal but truncates to nothing at all the octal 00.
Or...
command exec >&3 || handle_it
But if you're using ksh93
, you can just do:
fds
For a list of of open file descriptors. Add -l
to see where they go.
Your trick looks cute; but for an idiomatic way I wonder why you didn't use:
if ( exec 1>&3 ) 2>&-