I’d like to make a bash script output additional information to file descriptors (FDs) greater than or equal to 3, when they are open. To test whether an FD is open, I devised the following trick:
if (printf '' 1>&3) 2>&-; then # File descriptor 3 is open else # File descriptor 3 is not open fi
This is sufficient for my needs, but I’m curious as to whether there is a more idiomatic way of testing if an FD is valid. I’m especially interested about whether there exists a mapping of the fcntl(1) syscall to a shell command, which would allow the retrieval of FD flags (O_WRONLY and O_RDWR to test whether the FD is writable, and O_RDONLY and O_RDWR to test whether the FD is readable).
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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
Method 2
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-filedoes 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 newline 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.
Method 3
Open file descriptors can be found in /proc/<pid>/fd. To list, for example, the open file descriptors of the current shell you can issue ls -l /proc/$$/fd which should give you something like:
total 0 lrwx------ 1 testuser testuser 64 jun 1 09:11 0 -> /dev/pts/3 lrwx------ 1 testuser testuser 64 jun 1 09:11 1 -> /dev/pts/3 lrwx------ 1 testuser testuser 64 jun 1 09:11 2 -> /dev/pts/3 lrwx------ 1 testuser testuser 64 jun 1 09:39 255 -> /dev/pts/3
When you open a file using:
touch /tmp/myfile exec 7</tmp/myfile
It should be listed by a new ls -l /proc/$$/fd:
lr-x------ 1 testuser testuser 64 jun 1 09:11 7 -> /tmp/myfile
If you close the file descriptor again using exec 7>&- it is also not listed in /proc/$$/fd anymore.
Method 4
Your trick looks cute; but for an idiomatic way I wonder why you didn’t use:
if ( exec 1>&3 ) 2>&-
Method 5
For Linux atleast, [[ -e /dev/fd/${FD} ]] will do, where FD is the bash-variable that stores the FD you want to test. The command succeeds when FD is open, fails otherwise.
Demo:
exec 3<>/tmp/some-file
[[ -e /dev/fd/3 ]] && echo 'FD is open' || echo 'FD is closed'
exec 3<&- ; exec 3>&-
[[ -e /dev/fd/3 ]] && echo 'FD is open' || echo 'FD is closed'
Method 6
If you are interested in a low forking solution so as to use it repeatdly, I would suggest this function:
checkfd() {
exec 2>/dev/null
if exec >&3 ; then
exec 1>/dev/tty
echo "fd3 OK"
else
echo "fd3 KO"
fi
exec 2>/dev/tty
}
And here is what it produces with a zsh:
$ checkfd fd3 KO $ checkfd 3>/dev/null fd3 OK $
Method 7
The question is quite old – but anyway – why just do not use builtins?
for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done
Output:
0 is a valid FD 1 is a valid FD 2 is a valid FD 3 is INVALID FD 4 is INVALID FD 5 is INVALID FD
So, to answer the question – would suggest:
if [ -t 3 ]; then # File descriptor 3 is open else # File descriptor 3 is not open fi
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0