How do I find the next available file descriptor in Bash?

How can I find out if a file descriptor is being used in Bash? For example, if I have a script that reads, writes and closes fd 3, like

exec 3< <(some command here)
...
cat <&3
exec 3>&-

      

What's the best way to ensure that I'm not tampering with any other target for a handle that may have been set before my script was run? Should I be putting my entire script in a subshell?

+6


source to share


4 answers


Cleanly, bash

you can use the following method to find out if a given file descriptor ( 3

) is available:

rco="$(true 2>/dev/null >&3; echo $?)"
rci="$(true 2>/dev/null <&3; echo $?)"
if [[ "${rco}${rci}" = "11" ]] ; then
    echo "Cannot read or write fd 3, hence okay to use"
fi

      

It basically works by checking if you can read or write a given file descriptor. Assuming you can't do this, it's probably ok to use.

As for finding the first free descriptor, you can use something like:

exec 3>/dev/null    # Testing, comment out to make
exec 4</dev/null    # descriptor available.

found=none
for fd in {0..200}; do
    rco="$(true 2>/dev/null >&${fd}; echo $?)"
    rci="$(true 2>/dev/null <&${fd}; echo $?)"
    [[ "${rco}${rci}" = "11" ]] && found=${fd} && break
done
echo "First free is ${found}"

      

Running this script gives 5

as the first free descriptor, but you can play with the lines exec

to see how to make the earlier one available so the code snippet can find it.


As noted in the comments, systems providing procfs

(filesystem /proc

) have a different way of finding free descriptors. The directory /proc/PID/fd

will contain an entry for each open file descriptor as follows:



pax> ls -1 /proc/$$/fd
0
1
2
255

      

So, you can use a script similar to the one above to find a free entry there:

exec 3>/dev/null    # Testing, comment out to make
exec 4</dev/null    #   descriptor available.

found=none
for fd in {0..200} ; do
    [[ ! -e /proc/$$/fd/${fd} ]] && found=${fd} && break
done
echo "First free is ${found}"

      

Just keep in mind that not all systems providing bash

will necessarily have procfs

(examples BDS and CygWin are examples). Should be fine for Linux, if one is for OS.


Of course, you still have the option of wrapping your entire shell script with something like:

(
    # Your current script goes here
)

      

In this case, the file descriptors will be stored outside these parentheses, and you can manipulate them internally as you see fit.

+7


source


If you don't care if the file descriptor is higher than 9, you can ask the shell to provide it. Of course, fd is guaranteed to be free of its own shell.

Function available since bash 4.1+ (2009-12-31) {varname} style of automatic file descriptor description

$ exec {var}>hellofile
$ echo "$var"
15

$ echo "this is a test" >&${var}
$ cat hellofile
this is a test

$ exec {var}>&-                      # to close the fd.

      



In fact, on linux, you can see the open fds with:

$ ls /proc/$$/fd
0 1 2 255

      

+8


source


Another answer that uses pre-bash-4.1 syntax does a lot of unnecessary subshell spawning and redundant checks. It also has an arbitrary shorthand for the maximum FD number.

The following code should do the trick without spawning a subshell (except to call ulimit

if we want a decent upper limit for FD numbers).

fd=2 max=$(ulimit -n)

while ((++fd < max)); do
   ! true <&$fd && break
done 2>/dev/null && echo $fd

      

  • Basically, we just iterate over the possible FDs until we get to the point where we can't cheat.
  • In order to avoid Bad file descriptor

    the error message from the last iteration of the loop, we redirect the entire error stream into the while

    loop.
+7


source


For those who prefer one liners and don't have Bash-4. 1+ available:

{ seq 0 255; ls -1 /proc/$$/fd; } | sort -n | uniq -u | head -1

      

0


source







All Articles