测试文件描述符是否有效(用于输入)

Kus*_*nda 5 shell file-descriptors

在问题“测试文件描述符是否有效”中,寻求测试是否打开文件描述符。

答案都集中在测试文件描述符是否为输出打开,但如何测试文件描述符是否为输入打开?

这是在另一个问题的答案的评论线程中出现的,答案是这样解释的,

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
Run Code Online (Sandbox Code Playgroud)

问题[ ! -t 0 ]在于,-t如果文件描述符打开与终端关联,则测试为真。如果测试为假,则描述符要么关闭,要么不与终端关联(即我们正在从管道或重定向读取)。[ ! -t 0 ]因此,测试并不能保证文件描述符甚至有效。

如何确定它是否有效(这样read就不会抱怨)或它是否已关闭?

mos*_*svy 5

在 C 中使用read(fd, 0, 0)或很容易进行检查(fcntl(fd, F_GETFL) & O_WRONLY) == 0。我无法欺骗任何标准实用程序来做到这一点,所以这里有一些可能的解决方法。

在 linux 上,您可以使用/proc/PID/fdinfo/FD

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

在 OpenBSD 和 NetBSD 上,您可以使用/dev/fd/FDdd零计数:

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

在 FreeBSD 上,默认只提供前 3 个 fds /dev/fd;您应该安装fdescfs(5)/dev/fd或:

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

笔记:

在某些系统上,bash它的模拟/dev/fd/FD,因此cat </dev/fd/7可能与cat /dev/fd/7. 同样的警告适用于gawk.

read(2)长度为0(或open(2)没有O_TRUNC在其flags)将更新访问时间或其他任何时间戳。

在Linux上,一个read(2)总是失败的一个目录,即使它被打开了,而不O_DIRECTORY标志。在其他 Unix 系统上,一个目录可能像另一个文件一样被读取。

标准未指定是否从文件中dd count=0复制任何块或所有块:前者是 GNU dd ( gdd) 和ddfrom *BSD 的行为。