测试文件描述符是否有效

Wit*_*iko 17 bash file-descriptors

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

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

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

Sté*_*las 16

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

if print -nu3; then
  echo fd 3 is writeable
fi
Run Code Online (Sandbox Code Playgroud)

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

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing
Run Code Online (Sandbox Code Playgroud)

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

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

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

if { true >&3; } 2<> /dev/null
Run Code Online (Sandbox Code Playgroud)

而是避免分叉(除了在 Bourne shell 中,重定向复合命令总是会导致子 shell)。不要使用:而不是true因为它是一个特殊的内置函数,所以当 bash 处于 POSIX 合规模式时会导致 shell 退出。

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

if { >&3; } 2<> /dev/null
Run Code Online (Sandbox Code Playgroud)

  • @mikeserve,回复:您的编辑,`&lt;&gt;` 是什么?shell 不会从它的 stderr 中读取数据,为什么要以 read+write 方式打开它?你所说的*内在发生了什么?*是什么意思? (4认同)

mik*_*erv 7

在 POSIX应用程序使用说明中,您将找到以下内容:command

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

command exec > unwritable-file
Run Code Online (Sandbox Code Playgroud)

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

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

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null
Run Code Online (Sandbox Code Playgroud)

或者...

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

它将写入字符串后跟一个\newline 到 stdout 或 3 并且在 3 未打开时仍然传递非零退出状态,因为完成的数学运算$?最终未能将八进制08转换为%decimal但截断为空八进制00

或者...

command exec >&3 || handle_it
Run Code Online (Sandbox Code Playgroud)

但是,如果您正在使用ksh93,则可以执行以下操作:

fds
Run Code Online (Sandbox Code Playgroud)

获取打开的文件描述符列表。添加-l以查看它们的去向。