Testing if a file descriptor is valid (for input)

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP

up vote
4
down vote

favorite

1

In the question “Testing if a file descriptor is valid”, a test is sought for testing whether a file descriptor is opened or not.

The answers all focus on testing whether the file descriptor is opened for output, but how may one test whether the file descriptor is opened for input?

This came up in a comment thread for an answer to another question, where the answer said, paraphrasing,

if [ -n "$1" ]; then
    # read input from file "$1" (we're assuming it exists)
elif [ ! -t 0 ]; then
    # read input from standard input (from pipe or redirection)
else
    # no input given (we don't want to read from the terminal)
fi

The problem with [ ! -t 0 ] is that the -t test is true if the file descriptor is open and associated with a terminal. If the test is false, then the descriptor is either closed, or not associated with a terminal (i.e. we’re reading from a pipe or redirection). Testing with [ ! -t 0 ] is therefore not a guarantee that the file descriptor is even valid.

How to determine whether it’s valid (so that read would not complain) or whether it’s closed?

share|improve this question

    up vote
    4
    down vote

    favorite

    1

    In the question “Testing if a file descriptor is valid”, a test is sought for testing whether a file descriptor is opened or not.

    The answers all focus on testing whether the file descriptor is opened for output, but how may one test whether the file descriptor is opened for input?

    This came up in a comment thread for an answer to another question, where the answer said, paraphrasing,

    if [ -n "$1" ]; then
        # read input from file "$1" (we're assuming it exists)
    elif [ ! -t 0 ]; then
        # read input from standard input (from pipe or redirection)
    else
        # no input given (we don't want to read from the terminal)
    fi
    

    The problem with [ ! -t 0 ] is that the -t test is true if the file descriptor is open and associated with a terminal. If the test is false, then the descriptor is either closed, or not associated with a terminal (i.e. we’re reading from a pipe or redirection). Testing with [ ! -t 0 ] is therefore not a guarantee that the file descriptor is even valid.

    How to determine whether it’s valid (so that read would not complain) or whether it’s closed?

    share|improve this question

      up vote
      4
      down vote

      favorite

      1

      up vote
      4
      down vote

      favorite

      1
      1

      In the question “Testing if a file descriptor is valid”, a test is sought for testing whether a file descriptor is opened or not.

      The answers all focus on testing whether the file descriptor is opened for output, but how may one test whether the file descriptor is opened for input?

      This came up in a comment thread for an answer to another question, where the answer said, paraphrasing,

      if [ -n "$1" ]; then
          # read input from file "$1" (we're assuming it exists)
      elif [ ! -t 0 ]; then
          # read input from standard input (from pipe or redirection)
      else
          # no input given (we don't want to read from the terminal)
      fi
      

      The problem with [ ! -t 0 ] is that the -t test is true if the file descriptor is open and associated with a terminal. If the test is false, then the descriptor is either closed, or not associated with a terminal (i.e. we’re reading from a pipe or redirection). Testing with [ ! -t 0 ] is therefore not a guarantee that the file descriptor is even valid.

      How to determine whether it’s valid (so that read would not complain) or whether it’s closed?

      share|improve this question

      In the question “Testing if a file descriptor is valid”, a test is sought for testing whether a file descriptor is opened or not.

      The answers all focus on testing whether the file descriptor is opened for output, but how may one test whether the file descriptor is opened for input?

      This came up in a comment thread for an answer to another question, where the answer said, paraphrasing,

      if [ -n "$1" ]; then
          # read input from file "$1" (we're assuming it exists)
      elif [ ! -t 0 ]; then
          # read input from standard input (from pipe or redirection)
      else
          # no input given (we don't want to read from the terminal)
      fi
      

      The problem with [ ! -t 0 ] is that the -t test is true if the file descriptor is open and associated with a terminal. If the test is false, then the descriptor is either closed, or not associated with a terminal (i.e. we’re reading from a pipe or redirection). Testing with [ ! -t 0 ] is therefore not a guarantee that the file descriptor is even valid.

      How to determine whether it’s valid (so that read would not complain) or whether it’s closed?

      shell file-descriptors

      share|improve this question

      share|improve this question

      share|improve this question

      share|improve this question

      edited Nov 28 at 23:36

      asked Nov 28 at 23:01

      Kusalananda

      119k16223364

      119k16223364

          1 Answer
          1

          active

          oldest

          votes

          up vote
          3
          down vote

          accepted

          The check is easy to do in C with either read(fd, 0, 0) or (fcntl(fd, F_GETFL) & O_WRONLY) == 0. I wasn’t able to trick any standard utility into doing just that, so here are some possible workarounds.

          On linux, you can use /proc/PID/fdinfo/FD:

          if [ ! -d "/proc/$$/fd/$fd" ] && grep -sq '^flags.*[02]$' "/proc/$$/fdinfo/$fd"; then
              echo "$fd" is valid for input
          fi
          

          On OpenBSD, NetBSD and Solaris, you can use /dev/fd/FD and dd with a 0 count:

          if dd if=/dev/fd/"$fd" count=0 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          On FreeBSD, only the first 3 fds are provided by default in /dev/fd; you should either mount fdescfs(5) on /dev/fd or:

          if (dd if=/dev/fd/0 count=0 <&"$fd") 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          Notes:

          On some systems, bash does its emulation of /dev/fd/FD, and so cat </dev/fd/7 may work completely different from cat /dev/fd/7. Same caveat applies to gawk.

          A read(2) with length 0 (or an open(2) without O_TRUNC in its flags) will not update the access time or any other timestamps.

          On linux, a read(2) will always fail on a directory, even if it was opened without the O_DIRECTORY flag. On other Unix systems, a directory can be read just like another file.

          share|improve this answer

          • It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
            – Stéphane Chazelas
            Nov 29 at 14:29

          • Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
            – mosvy
            Nov 29 at 15:08

          • Ah! Good catch.
            – Stéphane Chazelas
            Nov 29 at 16:20

          Your Answer

          StackExchange.ready(function() {
          var channelOptions = {
          tags: “”.split(” “),
          id: “106”
          };
          initTagRenderer(“”.split(” “), “”.split(” “), channelOptions);

          StackExchange.using(“externalEditor”, function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using(“snippets”, function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: ‘answer’,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: “”,
          imageUploader: {
          brandingHtml: “Powered by u003ca class=”icon-imgur-white” href=”https://imgur.com/”u003eu003c/au003e”,
          contentPolicyHtml: “User contributions licensed under u003ca href=”https://creativecommons.org/licenses/by-sa/3.0/”u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href=”https://stackoverflow.com/legal/content-policy”u003e(content policy)u003c/au003e”,
          allowUrls: true
          },
          onDemand: true,
          discardSelector: “.discard-answer”
          ,immediatelyShowMarkdownHelp:true
          });

          }
          });

          draft saved
          draft discarded

          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin(‘.new-post-login’, ‘https%3a%2f%2funix.stackexchange.com%2fquestions%2f484789%2ftesting-if-a-file-descriptor-is-valid-for-input%23new-answer’, ‘question_page’);
          }
          );

          Post as a guest

          Required, but never shown

          1 Answer
          1

          active

          oldest

          votes

          1 Answer
          1

          active

          oldest

          votes

          active

          oldest

          votes

          active

          oldest

          votes

          up vote
          3
          down vote

          accepted

          The check is easy to do in C with either read(fd, 0, 0) or (fcntl(fd, F_GETFL) & O_WRONLY) == 0. I wasn’t able to trick any standard utility into doing just that, so here are some possible workarounds.

          On linux, you can use /proc/PID/fdinfo/FD:

          if [ ! -d "/proc/$$/fd/$fd" ] && grep -sq '^flags.*[02]$' "/proc/$$/fdinfo/$fd"; then
              echo "$fd" is valid for input
          fi
          

          On OpenBSD, NetBSD and Solaris, you can use /dev/fd/FD and dd with a 0 count:

          if dd if=/dev/fd/"$fd" count=0 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          On FreeBSD, only the first 3 fds are provided by default in /dev/fd; you should either mount fdescfs(5) on /dev/fd or:

          if (dd if=/dev/fd/0 count=0 <&"$fd") 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          Notes:

          On some systems, bash does its emulation of /dev/fd/FD, and so cat </dev/fd/7 may work completely different from cat /dev/fd/7. Same caveat applies to gawk.

          A read(2) with length 0 (or an open(2) without O_TRUNC in its flags) will not update the access time or any other timestamps.

          On linux, a read(2) will always fail on a directory, even if it was opened without the O_DIRECTORY flag. On other Unix systems, a directory can be read just like another file.

          share|improve this answer

          • It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
            – Stéphane Chazelas
            Nov 29 at 14:29

          • Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
            – mosvy
            Nov 29 at 15:08

          • Ah! Good catch.
            – Stéphane Chazelas
            Nov 29 at 16:20

          up vote
          3
          down vote

          accepted

          The check is easy to do in C with either read(fd, 0, 0) or (fcntl(fd, F_GETFL) & O_WRONLY) == 0. I wasn’t able to trick any standard utility into doing just that, so here are some possible workarounds.

          On linux, you can use /proc/PID/fdinfo/FD:

          if [ ! -d "/proc/$$/fd/$fd" ] && grep -sq '^flags.*[02]$' "/proc/$$/fdinfo/$fd"; then
              echo "$fd" is valid for input
          fi
          

          On OpenBSD, NetBSD and Solaris, you can use /dev/fd/FD and dd with a 0 count:

          if dd if=/dev/fd/"$fd" count=0 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          On FreeBSD, only the first 3 fds are provided by default in /dev/fd; you should either mount fdescfs(5) on /dev/fd or:

          if (dd if=/dev/fd/0 count=0 <&"$fd") 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          Notes:

          On some systems, bash does its emulation of /dev/fd/FD, and so cat </dev/fd/7 may work completely different from cat /dev/fd/7. Same caveat applies to gawk.

          A read(2) with length 0 (or an open(2) without O_TRUNC in its flags) will not update the access time or any other timestamps.

          On linux, a read(2) will always fail on a directory, even if it was opened without the O_DIRECTORY flag. On other Unix systems, a directory can be read just like another file.

          share|improve this answer

          • It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
            – Stéphane Chazelas
            Nov 29 at 14:29

          • Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
            – mosvy
            Nov 29 at 15:08

          • Ah! Good catch.
            – Stéphane Chazelas
            Nov 29 at 16:20

          up vote
          3
          down vote

          accepted

          up vote
          3
          down vote

          accepted

          The check is easy to do in C with either read(fd, 0, 0) or (fcntl(fd, F_GETFL) & O_WRONLY) == 0. I wasn’t able to trick any standard utility into doing just that, so here are some possible workarounds.

          On linux, you can use /proc/PID/fdinfo/FD:

          if [ ! -d "/proc/$$/fd/$fd" ] && grep -sq '^flags.*[02]$' "/proc/$$/fdinfo/$fd"; then
              echo "$fd" is valid for input
          fi
          

          On OpenBSD, NetBSD and Solaris, you can use /dev/fd/FD and dd with a 0 count:

          if dd if=/dev/fd/"$fd" count=0 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          On FreeBSD, only the first 3 fds are provided by default in /dev/fd; you should either mount fdescfs(5) on /dev/fd or:

          if (dd if=/dev/fd/0 count=0 <&"$fd") 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          Notes:

          On some systems, bash does its emulation of /dev/fd/FD, and so cat </dev/fd/7 may work completely different from cat /dev/fd/7. Same caveat applies to gawk.

          A read(2) with length 0 (or an open(2) without O_TRUNC in its flags) will not update the access time or any other timestamps.

          On linux, a read(2) will always fail on a directory, even if it was opened without the O_DIRECTORY flag. On other Unix systems, a directory can be read just like another file.

          share|improve this answer

          The check is easy to do in C with either read(fd, 0, 0) or (fcntl(fd, F_GETFL) & O_WRONLY) == 0. I wasn’t able to trick any standard utility into doing just that, so here are some possible workarounds.

          On linux, you can use /proc/PID/fdinfo/FD:

          if [ ! -d "/proc/$$/fd/$fd" ] && grep -sq '^flags.*[02]$' "/proc/$$/fdinfo/$fd"; then
              echo "$fd" is valid for input
          fi
          

          On OpenBSD, NetBSD and Solaris, you can use /dev/fd/FD and dd with a 0 count:

          if dd if=/dev/fd/"$fd" count=0 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          On FreeBSD, only the first 3 fds are provided by default in /dev/fd; you should either mount fdescfs(5) on /dev/fd or:

          if (dd if=/dev/fd/0 count=0 <&"$fd") 2>/dev/null; then
              echo "$fd" is valid for input
          fi
          

          Notes:

          On some systems, bash does its emulation of /dev/fd/FD, and so cat </dev/fd/7 may work completely different from cat /dev/fd/7. Same caveat applies to gawk.

          A read(2) with length 0 (or an open(2) without O_TRUNC in its flags) will not update the access time or any other timestamps.

          On linux, a read(2) will always fail on a directory, even if it was opened without the O_DIRECTORY flag. On other Unix systems, a directory can be read just like another file.

          share|improve this answer

          share|improve this answer

          share|improve this answer

          edited Nov 29 at 19:21

          answered Nov 29 at 13:20

          mosvy

          5,1791323

          5,1791323

          • It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
            – Stéphane Chazelas
            Nov 29 at 14:29

          • Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
            – mosvy
            Nov 29 at 15:08

          • Ah! Good catch.
            – Stéphane Chazelas
            Nov 29 at 16:20

          • It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
            – Stéphane Chazelas
            Nov 29 at 14:29

          • Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
            – mosvy
            Nov 29 at 15:08

          • Ah! Good catch.
            – Stéphane Chazelas
            Nov 29 at 16:20

          It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
          – Stéphane Chazelas
          Nov 29 at 14:29

          It’s the open() that fails on BSDs, so you migh as well do if (true < /dev/fd/0) 2> /dev/null <&"$fd" (assuming fd is not 2).
          – Stéphane Chazelas
          Nov 29 at 14:29

          Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
          – mosvy
          Nov 29 at 15:08

          Thanks, but that would return true if the shell is bash, which does its own implementation of /dev/fd. Example: bash -c '(true </dev/fd/0) <&7 && echo fine' 7>/dev/null
          – mosvy
          Nov 29 at 15:08

          Ah! Good catch.
          – Stéphane Chazelas
          Nov 29 at 16:20

          Ah! Good catch.
          – Stéphane Chazelas
          Nov 29 at 16:20

          draft saved
          draft discarded

          Thanks for contributing an answer to Unix & Linux Stack Exchange!

          • Please be sure to answer the question. Provide details and share your research!

          But avoid

          • Asking for help, clarification, or responding to other answers.
          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.

          Some of your past answers have not been well-received, and you’re in danger of being blocked from answering.

          Please pay close attention to the following guidance:

          • Please be sure to answer the question. Provide details and share your research!

          But avoid

          • Asking for help, clarification, or responding to other answers.
          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.

          draft saved

          draft discarded

          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin(‘.new-post-login’, ‘https%3a%2f%2funix.stackexchange.com%2fquestions%2f484789%2ftesting-if-a-file-descriptor-is-valid-for-input%23new-answer’, ‘question_page’);
          }
          );

          Post as a guest

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Required, but never shown

          Related Post

          Leave a Reply

          Your email address will not be published. Required fields are marked *