测试文件描述符是否有效

测试文件描述符是否有效

我想让 bash 脚本在打开时向大于或等于 3 的文件描述符 (FD) 输出附加信息。为了测试 FD 是否打开,我设计了以下技巧:

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

这足以满足我的需求,但我很好奇是否有更惯用的方法来测试 FD 是否有效。我特别感兴趣的是是否存在系统调用到 shell 命令的映射fcntl(1),这将允许检索 FD 标志(O_WRONLYO_RDWR测试 FD 是否可写,并O_RDONLY测试 O_RDWRFD 是否可读)。

答案1

ksh(AT&T 和 pdksh 变体)或中zsh,您可以执行以下操作:

if print -nu3; then
  echo fd 3 is writeable
fi

他们不会在该 fd 上写入任何内容,但仍然检查该 fd 是否可写(使用fcntl(3, F_GETFL)),否则报告错误:

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(您可以重定向到/dev/null)。

对于bash,我认为您唯一的选择是检查 a 是否dup()像您的方法一样成功,尽管这不能保证 fd 可写(或调用外部实用程序(zsh/ perl...)来执行fcntl())。

请注意,在bash(像大多数 shell 一样)中,如果您使用(...)代替{...;},则会分叉一个额外的进程。您可以使用:

if { true >&3; } 2> /dev/null

而是为了避免分叉(除了在 Bourne shell 中,重定向复合命令总是会导致子 shell)。不要使用:代替,true因为那是特别的内置的,因此当 bash 处于 POSIX 合规模式时会导致 shell 退出。

但是,您可以将其缩短为:

if { >&3; } 2> /dev/null

答案2

在 POSIX 中command 应用程序使用描述你会发现以下内容:

有时抑制特殊内置函数的特殊特性有一些优点。例如:

command exec > unwritable-file

不会导致非交互式脚本中止,因此可以通过脚本检查输出状态。

这就是为什么你可以这样做:

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null

或者...

{ command >&3
  printf %s\\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null

哪个会写细绳后跟一个\newline 到 stdout 或 3,并且当 3 未打开时仍然传递非零退出状态,因为完成的数学$?最终无法转换八进制08%十进制但将八进制截断为空00

或者...

command exec >&3 || handle_it

但如果你正在使用ksh93,你可以这样做:

fds

用于打开文件描述符的列表。添加-l以查看它们去了哪里。

答案3

打开的文件描述符可以在 中找到/proc/<pid>/fd。例如,要列出当前 shell 的打开文件描述符,您可以发出ls -l /proc/$$/fd如下命令:

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

当您使用以下命令打开文件时:

touch /tmp/myfile
exec 7</tmp/myfile

它应该由新的列出ls -l /proc/$$/fd

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

如果您再次使用它关闭文件描述符,exec 7>&-它也不再列出/proc/$$/fd

答案4

这个问题很老了 - 但无论如何 - 为什么不使用内置函数?

for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done

输出:

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

因此,回答这个问题 - 建议:

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

相关内容