In Bash, how to find the lowest-numbered unused file descriptor?

后端 未结 6 1759
梦毁少年i
梦毁少年i 2021-01-30 16:45

In a Bash-script, is it possible to open a file on \"the lowest-numbered file descriptor not yet in use\"?

I have looked around for how to do this, but it seems that Bas

6条回答
  •  孤城傲影
    2021-01-30 17:23

    Apple Mac OS X is not Linux. I don't see any '/proc' file system on OS X.

    I guess one answer is to use "zsh", but I want to have a script that works on both OS X (aka BSD) and Linux in "bash". So, here I am, in the year 2020, with the latest version of OS X, which at this moment is Catalina, and I realize that Apple seems to have abandoned maintenance of Bash long ago; apparently in favor of Zsh.

    Here is my multi-OS solution to find the lowest unused file descriptor on Apple Mac OS X or Linux. I created an entire Perl script, and in-lined it into the Shell script. There must be a better way, but for now, this works for me.

    lowest_unused_fd() {
      # For "bash" version 4.1 and higher, and for "zsh", this entire function  
      # is replaced by the more modern operator "{fd}", used like this:
      #    exec {FD}>myFile.txt; echo "hello" >&$FD;
      if [ $(uname) = 'Darwin' ] ; then
        lsof -p $$ -a -d 0-32 | perl -an \
          -e 'BEGIN { our @currentlyUsedFds; };' \
          -e '(my $digits = $F[3]) =~ s/\D//g;' \
          -e 'next if $digits eq "";' \
          -e '$currentlyUsedFds[$digits] = $digits;' \
          -e 'END { my $ix; 
                for( $ix=3; $ix <= $#currentlyUsedFds; $ix++) {  
                  my $slotContents = $currentlyUsedFds[$ix];
                  if( !defined($slotContents) ) { 
                    last; 
                  } 
                } 
                print $ix;
              }' ;
      else 
        local FD=3
        while [ -e /proc/$$/fd/$FD ]; do
          FD=$((FD+1))
        done
        echo $FD
      fi;
    }
    

    The -an options to Perl tells it to (-n) run an implied while() loop that reads the file line by line and (-a) auto-split it into an array of words which, by convention, is named @F. The BEGIN says what to do before that while() loop, and the END says what to do after. The while() loop picks out field [3] of each line, reduces it to just its leading digits, which is a port number, and saves that in an array of port numbers that are currently in use, and therefore are unavailable. The END block then finds the lowest integer whose slot is not occupied.

    Update: After doing all that, I actually am not using this in my own code. I realized that the answer from KingPong and Bruno Bronsky is far more elegant. However, I will leave this answer in place; it might be interesting to somebody.

提交回复
热议问题