Testing if a file descriptor is valid

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-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 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

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x